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.
- package/ServiceWorker.mjs +2 -2
- package/apps/colors/view/GridContainer.mjs +1 -1
- package/apps/covid/view/AttributionComponent.mjs +1 -1
- package/apps/covid/view/HeaderContainer.mjs +6 -6
- package/apps/covid/view/MainContainerController.mjs +5 -5
- package/apps/covid/view/TableContainerController.mjs +1 -1
- package/apps/covid/view/country/Gallery.mjs +13 -13
- package/apps/covid/view/country/Helix.mjs +13 -13
- package/apps/covid/view/country/HistoricalDataTable.mjs +1 -1
- package/apps/email/view/Viewport.mjs +2 -2
- package/apps/form/view/SideNavList.mjs +1 -1
- package/apps/portal/index.html +1 -1
- package/apps/portal/resources/data/examples_devmode.json +26 -27
- package/apps/portal/resources/data/examples_dist_dev.json +26 -27
- package/apps/portal/resources/data/examples_dist_esm.json +25 -26
- package/apps/portal/resources/data/examples_dist_prod.json +26 -27
- package/apps/portal/view/HeaderToolbar.mjs +3 -3
- package/apps/portal/view/about/Container.mjs +2 -2
- package/apps/portal/view/about/MemberContainer.mjs +3 -3
- package/apps/portal/view/blog/List.mjs +7 -7
- package/apps/portal/view/examples/List.mjs +4 -4
- package/apps/portal/view/home/ContentBox.mjs +2 -2
- package/apps/portal/view/home/FeatureSection.mjs +3 -3
- package/apps/portal/view/home/FooterContainer.mjs +7 -7
- package/apps/portal/view/home/parts/AfterMath.mjs +3 -3
- package/apps/portal/view/home/parts/MainNeo.mjs +3 -3
- package/apps/portal/view/home/parts/References.mjs +6 -6
- package/apps/portal/view/learn/ContentComponent.mjs +102 -111
- package/apps/portal/view/learn/PageSectionsContainer.mjs +1 -1
- package/apps/portal/view/learn/PageSectionsList.mjs +2 -2
- package/apps/portal/view/services/Component.mjs +16 -16
- package/apps/realworld/view/FooterComponent.mjs +1 -1
- package/apps/realworld/view/HeaderComponent.mjs +8 -8
- package/apps/realworld/view/HomeComponent.mjs +6 -6
- package/apps/realworld/view/article/CommentComponent.mjs +4 -4
- package/apps/realworld/view/article/Component.mjs +14 -14
- package/apps/realworld/view/article/CreateCommentComponent.mjs +3 -3
- package/apps/realworld/view/article/CreateComponent.mjs +3 -3
- package/apps/realworld/view/article/PreviewComponent.mjs +1 -1
- package/apps/realworld/view/article/TagListComponent.mjs +2 -2
- package/apps/realworld/view/user/ProfileComponent.mjs +8 -8
- package/apps/realworld/view/user/SettingsComponent.mjs +4 -4
- package/apps/realworld/view/user/SignUpComponent.mjs +4 -4
- package/apps/realworld2/view/FooterComponent.mjs +1 -1
- package/apps/realworld2/view/HomeContainer.mjs +3 -3
- package/apps/realworld2/view/article/DetailsContainer.mjs +1 -1
- package/apps/realworld2/view/article/PreviewComponent.mjs +7 -7
- package/apps/realworld2/view/article/TagListComponent.mjs +2 -2
- package/apps/realworld2/view/user/ProfileContainer.mjs +1 -1
- package/apps/route/view/center/CardAdministration.mjs +2 -2
- package/apps/route/view/center/CardAdministrationDenied.mjs +1 -1
- package/apps/route/view/center/CardContact.mjs +2 -2
- package/apps/route/view/center/CardHome.mjs +1 -1
- package/apps/route/view/center/CardSection1.mjs +1 -1
- package/apps/route/view/center/CardSection2.mjs +1 -1
- package/apps/sharedcovid/view/AttributionComponent.mjs +1 -1
- package/apps/sharedcovid/view/HeaderContainer.mjs +6 -6
- package/apps/sharedcovid/view/MainContainerController.mjs +5 -5
- package/apps/sharedcovid/view/TableContainerController.mjs +1 -1
- package/apps/sharedcovid/view/country/Gallery.mjs +13 -13
- package/apps/sharedcovid/view/country/Helix.mjs +13 -13
- package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -1
- package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +1 -1
- package/apps/shareddialog/view/MainContainer.mjs +1 -1
- package/buildScripts/createApp.mjs +2 -2
- package/examples/table/cellEditing/MainContainer.mjs +1 -1
- package/examples/table/container/MainContainer.mjs +3 -3
- package/examples/table/nestedRecordFields/Viewport.mjs +6 -6
- package/examples/tableFiltering/MainContainer.mjs +1 -1
- package/examples/tablePerformance/MainContainer.mjs +1 -1
- package/examples/tablePerformance/MainContainer2.mjs +1 -1
- package/examples/tablePerformance/MainContainer3.mjs +2 -2
- package/examples/tableStore/MainContainer.mjs +2 -2
- package/learn/Glossary.md +261 -0
- package/learn/UsingTheseTopics.md +2 -2
- package/learn/benefits/ConfigSystem.md +538 -28
- package/learn/benefits/Effort.md +47 -2
- package/learn/benefits/Features.md +50 -32
- package/learn/benefits/FormsEngine.md +68 -38
- package/learn/benefits/MultiWindow.md +33 -7
- package/learn/benefits/OffTheMainThread.md +2 -2
- package/learn/benefits/Quick.md +45 -12
- package/learn/benefits/RPCLayer.md +75 -0
- package/learn/benefits/Speed.md +16 -11
- package/learn/gettingstarted/ComponentModels.md +4 -4
- package/learn/gettingstarted/Config.md +6 -6
- package/learn/gettingstarted/DescribingTheUI.md +4 -4
- package/learn/gettingstarted/Events.md +6 -6
- package/learn/gettingstarted/Extending.md +4 -4
- package/learn/gettingstarted/References.md +6 -6
- package/learn/gettingstarted/Workspaces.md +6 -6
- package/learn/guides/ApplicationBootstrap.md +26 -26
- package/learn/guides/ComponentsAndContainers.md +12 -12
- package/learn/guides/ConfigSystemDeepDive.md +280 -0
- package/learn/guides/CustomComponents.md +2 -2
- package/learn/guides/DeclarativeComponentTreesVsImperativeVdom.md +17 -17
- package/learn/guides/InstanceLifecycle.md +295 -1
- package/learn/guides/MainThreadAddons.md +475 -0
- package/learn/guides/PortalApp.md +2 -2
- package/learn/guides/StateProviders.md +12 -12
- package/learn/guides/WorkingWithVDom.md +14 -14
- package/learn/guides/events/CustomEvents.md +16 -16
- package/learn/guides/events/DomEvents.md +12 -12
- package/learn/javascript/ClassFeatures.md +3 -2
- package/learn/javascript/Classes.md +8 -8
- package/learn/javascript/NewNode.md +4 -4
- package/learn/javascript/Overrides.md +8 -8
- package/learn/javascript/Super.md +10 -8
- package/learn/tree.json +52 -51
- package/learn/tutorials/Earthquakes.md +54 -57
- package/learn/tutorials/TodoList.md +4 -4
- package/package.json +2 -2
- package/resources/scss/src/apps/portal/learn/ContentComponent.scss +12 -0
- package/resources/scss/src/table/{View.scss → Body.scss} +1 -1
- package/resources/scss/src/table/plugin/CellEditing.scss +1 -1
- package/resources/scss/theme-dark/table/{View.scss → Body.scss} +1 -1
- package/resources/scss/theme-light/table/{View.scss → Body.scss} +1 -1
- package/resources/scss/theme-neo-light/Global.scss +1 -2
- package/resources/scss/theme-neo-light/table/{View.scss → Body.scss} +1 -1
- package/src/DefaultConfig.mjs +2 -2
- package/src/Main.mjs +8 -7
- package/src/Neo.mjs +16 -2
- package/src/button/Base.mjs +2 -2
- package/src/calendar/view/SettingsContainer.mjs +2 -2
- package/src/calendar/view/YearComponent.mjs +9 -9
- package/src/calendar/view/calendars/ColorsList.mjs +1 -1
- package/src/calendar/view/calendars/List.mjs +1 -1
- package/src/calendar/view/month/Component.mjs +15 -15
- package/src/calendar/view/week/Component.mjs +12 -12
- package/src/calendar/view/week/EventDragZone.mjs +4 -4
- package/src/calendar/view/week/TimeAxisComponent.mjs +3 -3
- package/src/component/Base.mjs +17 -2
- package/src/component/Carousel.mjs +2 -2
- package/src/component/Chip.mjs +3 -3
- package/src/component/Circle.mjs +2 -2
- package/src/component/DateSelector.mjs +8 -8
- package/src/component/Helix.mjs +1 -1
- package/src/component/Label.mjs +3 -18
- package/src/component/Legend.mjs +3 -3
- package/src/component/MagicMoveText.mjs +6 -14
- package/src/component/Process.mjs +3 -3
- package/src/component/Progress.mjs +1 -1
- package/src/component/StatusBadge.mjs +2 -2
- package/src/component/Timer.mjs +2 -2
- package/src/component/Toast.mjs +5 -3
- package/src/container/AccordionItem.mjs +2 -2
- package/src/container/Base.mjs +1 -1
- package/src/core/Base.mjs +77 -14
- package/src/core/Util.mjs +14 -2
- package/src/date/DayViewComponent.mjs +2 -2
- package/src/date/SelectorContainer.mjs +1 -1
- package/src/draggable/grid/header/toolbar/SortZone.mjs +21 -21
- package/src/draggable/table/header/toolbar/SortZone.mjs +1 -1
- package/src/form/field/CheckBox.mjs +4 -4
- package/src/form/field/FileUpload.mjs +25 -39
- package/src/form/field/Range.mjs +1 -1
- package/src/form/field/Text.mjs +3 -3
- package/src/form/field/TextArea.mjs +2 -3
- package/src/grid/Body.mjs +8 -5
- package/src/grid/_export.mjs +1 -1
- package/src/list/Color.mjs +2 -2
- package/src/main/DeltaUpdates.mjs +157 -98
- package/src/main/addon/AmCharts.mjs +61 -84
- package/src/main/addon/Base.mjs +161 -42
- package/src/main/addon/GoogleMaps.mjs +9 -16
- package/src/main/addon/HighlightJS.mjs +2 -13
- package/src/main/addon/IntersectionObserver.mjs +21 -21
- package/src/main/addon/MonacoEditor.mjs +32 -64
- package/src/manager/ClassHierarchy.mjs +114 -0
- package/src/menu/List.mjs +1 -1
- package/src/plugin/Popover.mjs +2 -2
- package/src/sitemap/Component.mjs +1 -1
- package/src/table/{View.mjs → Body.mjs} +25 -22
- package/src/table/Container.mjs +43 -43
- package/src/table/_export.mjs +2 -2
- package/src/table/plugin/CellEditing.mjs +19 -19
- package/src/tooltip/Base.mjs +1 -6
- package/src/tree/Accordion.mjs +3 -3
- package/src/vdom/Helper.mjs +19 -22
- package/src/worker/App.mjs +1 -2
- package/src/worker/Base.mjs +7 -5
- package/src/worker/Canvas.mjs +2 -3
- package/src/worker/Data.mjs +5 -7
- package/src/worker/Task.mjs +2 -3
- package/src/worker/VDom.mjs +3 -4
- package/src/worker/mixin/RemoteMethodAccess.mjs +5 -2
- package/learn/guides/MainThreadAddonExample.md +0 -15
- package/learn/guides/MainThreadAddonIntro.md +0 -44
@@ -1,16 +1,27 @@
|
|
1
|
+
A Declarative Approach to Application Building
|
2
|
+
|
1
3
|
## Introduction
|
2
4
|
|
3
5
|
Modern JavaScript frameworks have revolutionized front-end development by providing declarative ways to build
|
4
6
|
user interfaces, primarily centered around enhancing HTML with custom syntax like JSX or Angular directives.
|
5
7
|
However, the complexity of applications extends far beyond the Document Object Model (DOM), encompassing crucial
|
6
8
|
non-DOM entities such as data stores, state providers, routers, view controllers, selection models, and layouts.
|
9
|
+
|
7
10
|
While existing frameworks offer solutions for managing these aspects, they often lack a truly consistent, declarative,
|
8
11
|
and nested approach to their configuration, a gap that a class config system aims to fill.
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
### The Problem with Disparate Configuration
|
14
|
+
|
15
|
+
Currently, the configuration and management of these non-DOM entities can feel somewhat disparate across different
|
16
|
+
frameworks. State management, for instance, might involve dedicated libraries (like Redux or Vuex), routing is handled
|
17
|
+
by router-specific configurations, and layouts might be defined through a mix of component composition and potentially
|
18
|
+
separate layout configurations. While these solutions are functional, they don't always present a unified configuration
|
19
|
+
tree that mirrors the nested, hierarchical structure often used for describing the UI. The syntax and patterns for
|
20
|
+
configuring a data store can be quite different from those used to define a route or a view controller.
|
12
21
|
|
13
|
-
|
22
|
+
Consider this Angular code snippet (from a new public API draft):
|
23
|
+
|
24
|
+
```javascript readonly
|
14
25
|
// MyComponent with an attribute
|
15
26
|
<MyComponent myAttribute="someValue" />
|
16
27
|
|
@@ -25,40 +36,539 @@ I recently found this Angular code snippet (new public API draft) on LinkedIn:
|
|
25
36
|
|
26
37
|
// Scoped inputs for MyDirective
|
27
38
|
<MyComponent @MyDirective(input1="someString" [input2]="mySignal()") />
|
28
|
-
|
39
|
+
```
|
29
40
|
|
30
|
-
|
41
|
+
This example illustrates how various aspects (attributes, input bindings, directives, host elements, scoped inputs) are
|
42
|
+
configured using distinct syntax patterns. While functional, this variety can increase the cognitive load on developers,
|
43
|
+
requiring them to constantly context-switch between different configuration paradigms for different elements.
|
31
44
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
a
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Imagine defining your application's data stores, their initial states, and how they relate to each other, alongside
|
45
|
-
the routes of your application, the view controllers responsible for handling those routes, and the layouts they will
|
46
|
-
use – all within a unified configuration syntax. This nested structure would clearly illustrate the dependencies and
|
45
|
+
More importantly, the entire syntax is strictly limited to DOM-based entities.
|
46
|
+
|
47
|
+
### Neo.mjs's Solution: A Unified Class Config System
|
48
|
+
|
49
|
+
This is where the benefit of a class config system becomes apparent. The vision in Neo.mjs is a system that allows
|
50
|
+
developers to describe the desired state and relationships of all application components—regardless of whether they
|
51
|
+
directly interact with the DOM—using a consistent, declarative, and nested configuration structure.
|
52
|
+
|
53
|
+
Imagine defining your application's data stores, their initial states, and how they relate to each other, alongside the
|
54
|
+
routes of your application, the view controllers responsible for handling those routes, and the layouts they will
|
55
|
+
use – all within a unified configuration syntax. This nested structure clearly illustrates the dependencies and
|
47
56
|
composition of the application's various parts, offering a holistic view that is often obscured when non-DOM elements
|
48
57
|
are configured in isolation using different mechanisms.
|
49
58
|
|
50
|
-
|
59
|
+
Neo.mjs achieves this by leveraging a powerful static config object pattern. You define properties and their default
|
60
|
+
values directly within your class definitions. When you create an instance of that class using Neo.create(), you can
|
61
|
+
override these defaults, and the framework automatically processes these configurations to instantiate and configure
|
62
|
+
components, data models, routes, and more. This mechanism applies consistently across all Neo.mjs classes.
|
63
|
+
|
64
|
+
## Key Advantages
|
65
|
+
|
51
66
|
A class config system, by treating all application entities as configurable classes within a unified hierarchy,
|
52
67
|
offers several key advantages:
|
53
68
|
|
54
|
-
*
|
55
|
-
|
56
|
-
|
57
|
-
*
|
58
|
-
|
59
|
-
|
69
|
+
* **Consistency**: Provides a single, predictable way to configure any part of the application, reducing the cognitive
|
70
|
+
load on developers who would otherwise need to learn and context-switch between different configuration paradigms for DOM and non-DOM elements.
|
71
|
+
|
72
|
+
* **Declarative Clarity**: Enables developers to declare the desired state and relationships of their application's
|
73
|
+
components in a clear and concise manner, rather than writing imperative code to set up and connect these entities.
|
74
|
+
This significantly improves readability and maintainability.
|
75
|
+
|
76
|
+
* **Nested Structure**: Allows for the natural expression of hierarchical relationships between components, whether they
|
77
|
+
are parent-child UI elements or a router managing various routes, each with associated view controllers and data requirements.
|
78
|
+
This mirrors the often tree-like structure of applications.
|
79
|
+
|
80
|
+
* **Improved Maintainability**: Changes to the application's structure or behavior can be made in a centralized and
|
81
|
+
organized configuration, rather than spread across various imperative code snippets and disparate configuration files.
|
82
|
+
This leads to a more predictable and manageable codebase.
|
83
|
+
|
84
|
+
* **Enhanced Tooling and Abstraction**: A unified system provides a solid foundation for building powerful development tools,
|
85
|
+
such as visual editors or automatic documentation generators, that can understand and manipulate the entire application's
|
86
|
+
structure. It also allows for higher levels of abstraction, potentially simplifying the definition of complex application patterns.
|
87
|
+
|
88
|
+
* **Reactive Configuration & Lifecycle Hooks:**: Neo.mjs configs are inherently reactive. Similar to how UI frameworks
|
89
|
+
react to state changes and update the DOM, Neo.mjs's reactive class config system automatically updates views when config
|
90
|
+
values change. For instance, simply assigning a new value to a config directly:
|
91
|
+
|
92
|
+
```javascript readonly
|
93
|
+
myButton.text = 'New Button Text'; // UI will update automatically
|
94
|
+
myButton.iconCls = 'fa fa-check'; // UI will update automatically
|
95
|
+
```
|
96
|
+
|
97
|
+
For optimal performance when changing multiple configs simultaneously, it's recommended to use the `set()` method.
|
98
|
+
This ensures all changes are processed within a single, efficient update cycle, avoiding unnecessary redraws.
|
99
|
+
|
100
|
+
Furthermore, any config defined with a trailing underscore (e.g., `myConfig_`) automatically gains optional lifecycle hooks:
|
101
|
+
* `beforeGetMyConfig(value)`
|
102
|
+
* `beforeSetMyConfig(value, oldValue)`
|
103
|
+
* `afterSetMyConfig(value, oldValue)`
|
104
|
+
|
105
|
+
These powerful hooks allow you to intercept, validate,
|
106
|
+
transform, or react to config changes, providing fine-grained control over data flow and enabling clean side effects.
|
107
|
+
|
108
|
+
* **Direct DevTools Interaction**: The declarative and accessible nature of Neo.mjs class configs allows developers to
|
109
|
+
easily inspect, modify, and experiment with component and application state directly within the browser's developer
|
110
|
+
tools console. This live interaction capability significantly streamlines debugging, prototyping, and understanding
|
111
|
+
complex application behavior.
|
60
112
|
|
61
113
|
While existing frameworks have made significant strides in declarative UI development, the concept of extending this
|
62
114
|
declarative, nested configuration approach consistently to all aspects of an application, particularly the non-DOM realm,
|
63
|
-
represents a powerful next step.
|
115
|
+
represents a powerful next step. Neo.mjs's class config system holds the promise of a more unified, maintainable, and
|
64
116
|
understandable way to build complex modern web applications.
|
117
|
+
|
118
|
+
## Unifying Creation and Updates: The Consistent Config Experience
|
119
|
+
|
120
|
+
A particularly powerful aspect of the Neo.mjs config system's "unified" nature lies in its consistent application
|
121
|
+
across object creation and dynamic updates. The same declarative `static config` block that defines your class's properties
|
122
|
+
also serves as the blueprint for its instances.
|
123
|
+
|
124
|
+
When you initially create a component or any Neo.mjs class using `Neo.create()`, you pass a config object that directly
|
125
|
+
leverages this blueprint. Crucially, when you later need to change properties of an existing instance
|
126
|
+
(e.g., `myButton.set({ text: 'New Text' })` or `myButton.text = 'New Text'`),
|
127
|
+
you use the **exact same declarative config syntax**.
|
128
|
+
This consistency means developers only need to learn one powerful way to interact with an object's properties,
|
129
|
+
whether for initial setup or reactive modifications throughout its lifecycle. This predictability significantly
|
130
|
+
streamlines development, reduces cognitive load, and enhances code readability,
|
131
|
+
making the entire application more intuitive to manage.
|
132
|
+
|
133
|
+
## The static config Block: Your Declarative Blueprint
|
134
|
+
|
135
|
+
Every Neo.mjs class, from UI components to data models and utility classes, can define a `static config` object.
|
136
|
+
This object serves as the blueprint for instances of that class, declaring their properties, default values,
|
137
|
+
and how they interact.
|
138
|
+
|
139
|
+
### 1. Basic Component Configuration: The Neo.mjs Button
|
140
|
+
|
141
|
+
Let's start with a simple example: configuring a button. In Neo.mjs, even fundamental UI elements like buttons
|
142
|
+
are highly configurable classes.
|
143
|
+
|
144
|
+
Consider the `Neo.button.Base class`
|
145
|
+
[[Source: button.Base.mjs](https://github.com/neomjs/neo/blob/dev/src/button/Base.mjs)],
|
146
|
+
Its static config block defines all the properties you can set to customize a button's appearance and behavior:
|
147
|
+
|
148
|
+
```javascript readonly
|
149
|
+
// From: Neo.button.Base
|
150
|
+
class Button extends Component {
|
151
|
+
static config = {
|
152
|
+
className: 'Neo.button.Base',
|
153
|
+
ntype : 'button',
|
154
|
+
text : null, // The text displayed on the button
|
155
|
+
iconCls_ : null, // The CSS class to use for an icon, e.g. 'fa fa-home'
|
156
|
+
handler_ : null, // Shortcut for domListeners={click:handler}
|
157
|
+
route_ : null, // Change the browser hash value on click.
|
158
|
+
|
159
|
+
// The virtual DOM structure of the button
|
160
|
+
// (not used parts will not show up inside the live DOM)
|
161
|
+
_vdom: {
|
162
|
+
tag: 'button', type: 'button', cn: [
|
163
|
+
{tag: 'span', cls: ['neo-button-glyph']},
|
164
|
+
{tag: 'span', cls: ['neo-button-text']},
|
165
|
+
{cls: ['neo-button-badge']},
|
166
|
+
{cls: ['neo-button-ripple-wrapper'], cn: [
|
167
|
+
{cls: ['neo-button-ripple']}
|
168
|
+
]}
|
169
|
+
]
|
170
|
+
}
|
171
|
+
// ... many other properties like badgeText_, iconPosition_, menu_, pressed_, etc.
|
172
|
+
}
|
173
|
+
// ... rest of the class definition
|
174
|
+
}
|
175
|
+
```
|
176
|
+
|
177
|
+
### Instantiating and Configuring a Button
|
178
|
+
|
179
|
+
To create an instance of this button and apply your desired configuration, you use `Neo.create()` or `Neo.ntype()`:
|
180
|
+
|
181
|
+
```javascript readonly
|
182
|
+
import Button fron '../../src/button/Base.mjs';
|
183
|
+
|
184
|
+
// A simple button
|
185
|
+
Neo.create({
|
186
|
+
module : Button,
|
187
|
+
text : 'Click Me',
|
188
|
+
handler: () => alert('Button clicked!')
|
189
|
+
});
|
190
|
+
|
191
|
+
// A button with an icon and route
|
192
|
+
Neo.ntype({
|
193
|
+
ntype : 'button',
|
194
|
+
text : 'Go Home',
|
195
|
+
iconCls : 'fa fa-home', // Uses Font Awesome icon
|
196
|
+
iconPosition: 'left', // Icon on the left
|
197
|
+
route : '#main/home' // Changes browser hash to #main/home on click
|
198
|
+
});
|
199
|
+
```
|
200
|
+
|
201
|
+
Notice how `text`, `iconCls`, `iconPosition`, and `route` are simply properties passed into the configuration object.
|
202
|
+
The framework takes care of applying these values to the button instance and rendering its virtual DOM (`_vdom) accordingly.
|
203
|
+
|
204
|
+
### 2. Nested Component Configuration: Containers and the items Property
|
205
|
+
|
206
|
+
Neo.mjs components can be nested to create complex UIs, forming a hierarchical tree structure. This is managed consistently
|
207
|
+
through container components and their `items` configuration.
|
208
|
+
|
209
|
+
The `Neo.container.Base` class
|
210
|
+
[[Source: container.Base.mjs](https://github.com/neomjs/neo/blob/dev/src/container/Base.mjs)],
|
211
|
+
is fundamental for building UIs with multiple child components. It provides a `static config`
|
212
|
+
property called `items_`, which is an array or object containing configurations for its children. It also includes a `layout_`
|
213
|
+
property to define how these items are arranged.
|
214
|
+
|
215
|
+
Here's a simplified look at the relevant parts of `Neo.container.Base`'s config:
|
216
|
+
|
217
|
+
```javascript readonly
|
218
|
+
// From: Neo.container.Base
|
219
|
+
class Container extends Component {
|
220
|
+
static config = {
|
221
|
+
className: 'Neo.container.Base',
|
222
|
+
ntype: 'container',
|
223
|
+
// ... other properties
|
224
|
+
/**
|
225
|
+
* Defines the layout manager for the container's items.
|
226
|
+
* @member {Object|String|null} layout_={ntype: 'vbox', align: 'stretch'}
|
227
|
+
*/
|
228
|
+
layout_: {
|
229
|
+
ntype: 'vbox',
|
230
|
+
align: 'stretch'
|
231
|
+
},
|
232
|
+
/**
|
233
|
+
* An array or an object of config objects|instances|modules for each child component
|
234
|
+
* @member {Object[]} items_=[]
|
235
|
+
*/
|
236
|
+
items_: [],
|
237
|
+
// ... other properties
|
238
|
+
}
|
239
|
+
// ... rest of the class definition
|
240
|
+
}
|
241
|
+
```
|
242
|
+
|
243
|
+
The `items_` config allows for flexible ways to define children:
|
244
|
+
|
245
|
+
* **By ntype**: A string alias for the component's className.
|
246
|
+
* **By imported module**: Directly referencing the imported class.
|
247
|
+
* **By instance**: Passing an already created Neo.mjs component instance.
|
248
|
+
|
249
|
+
### Example: A Container with Nested Buttons
|
250
|
+
|
251
|
+
Let's create a container with a vertical box layout and several buttons inside it,
|
252
|
+
demonstrating different ways to configure items:
|
253
|
+
|
254
|
+
```javascript readonly
|
255
|
+
import Button from '../../src/button/Base.mjs'; // Assuming Button is imported correctly
|
256
|
+
|
257
|
+
Neo.ntype({
|
258
|
+
ntype: 'container',
|
259
|
+
layout: {
|
260
|
+
ntype: 'vbox', // Vertical box layout
|
261
|
+
align: 'center' // Center items horizontally
|
262
|
+
},
|
263
|
+
items: [ // Define child components within the 'items' array
|
264
|
+
{ // Configured by ntype
|
265
|
+
ntype: 'button',
|
266
|
+
text : 'Button 1 (by ntype)'
|
267
|
+
},
|
268
|
+
{ // Configured by imported module
|
269
|
+
module: Button,
|
270
|
+
text : 'Button 2 (by module)'
|
271
|
+
},
|
272
|
+
Neo.create({ // Configured by instance
|
273
|
+
module: Button,
|
274
|
+
text : 'Button 3 (by instance)'
|
275
|
+
}),
|
276
|
+
{ // Another button with a handler
|
277
|
+
className: 'Neo.button.Base',
|
278
|
+
text : 'Hello Alert',
|
279
|
+
handler : () => console.log('Hello from a nested button!')
|
280
|
+
}
|
281
|
+
]
|
282
|
+
});
|
283
|
+
```
|
284
|
+
|
285
|
+
This example clearly shows how the `items` config allows for a declarative, nested structure, effectively building a UI tree.
|
286
|
+
The `layout` config further specifies how these children are arranged within the container,
|
287
|
+
all using the same consistent configuration syntax.
|
288
|
+
|
289
|
+
Note: While this section demonstrates container configuration, a comprehensive guide on Neo.mjs containers and their
|
290
|
+
various layouts (e.g., `vbox`, `hbox`, `fit`, `card`, `grid`) will be provided in a separate, dedicated guide to maintain
|
291
|
+
a sharp focus on the core class config system here.
|
292
|
+
|
293
|
+
### 3. Non-DOM Entity Configuration: Data Stores and Models
|
294
|
+
|
295
|
+
Beyond UI components, the class config system extends seamlessly to non-DOM entities like data stores and models,
|
296
|
+
allowing for a unified declarative approach to your application's data layer.
|
297
|
+
|
298
|
+
**Defining a Data Store** (`Neo.data.Store`)
|
299
|
+
|
300
|
+
A `Neo.data.Store` manages collections of data records, often fetched from a server or generated client-side.
|
301
|
+
The `static config` for a store allows you to define its associated data model, filters, and other properties.
|
302
|
+
|
303
|
+
Here's an example from `Neo.examples.grid.bigData.MainStore`
|
304
|
+
[[Source: MainStore.mjs](https://github.com/neomjs/neo/blob/dev/examples/grid/bigData/MainStore.mjs)]:
|
305
|
+
|
306
|
+
```javascript readonly
|
307
|
+
import Model from './MainModel.mjs'; // Assuming MainModel.mjs defines a Neo.data.Model
|
308
|
+
import Store from '../../../src/data/Store.mjs';
|
309
|
+
|
310
|
+
class MainStore extends Store {
|
311
|
+
static config = {
|
312
|
+
className : 'Neo.examples.grid.bigData.MainStore',
|
313
|
+
ntype : 'mainstore', // Custom ntype for easy referencing
|
314
|
+
amountColumns_: 50,
|
315
|
+
amountRows_ : 1000,
|
316
|
+
|
317
|
+
// Define default filters declaratively
|
318
|
+
filters: [{
|
319
|
+
property: 'firstname',
|
320
|
+
operator: 'like',
|
321
|
+
value : null
|
322
|
+
}, {
|
323
|
+
property: 'lastname',
|
324
|
+
operator: 'like',
|
325
|
+
value : null
|
326
|
+
}],
|
327
|
+
model: Model // Associate a data model with this store
|
328
|
+
}
|
329
|
+
// ... other properties and methods like firstnames, lastnames, generateData, etc.
|
330
|
+
}
|
331
|
+
|
332
|
+
export default Neo.setupClass(MainStore);
|
333
|
+
```
|
334
|
+
|
335
|
+
In this example:
|
336
|
+
|
337
|
+
* `amountColumns_` and `amountRows_` define initial data dimensions.
|
338
|
+
* The `filters` array declaratively sets up default filtering rules.
|
339
|
+
* The `model` property specifies which `Neo.data.Model` class instances in this store will adhere to.
|
340
|
+
This creates a powerful, type-safe data structure.
|
341
|
+
|
342
|
+
**Connecting a Store to a Grid**
|
343
|
+
|
344
|
+
One common use case is connecting a data store to a UI component like a grid. `The Neo.grid.Container`
|
345
|
+
(which GridContainer extends) also uses the class config system to specify its data `store`.
|
346
|
+
|
347
|
+
Here's how `Neo.examples.grid.bigData.GridContainer`
|
348
|
+
[[Source: GridContainer.mjs](https://github.com/neomjs/neo/blob/dev/examples/grid/bigData/GridContainer.mjs)]
|
349
|
+
connects to the `MainStore`:
|
350
|
+
|
351
|
+
```javascript readonly
|
352
|
+
import BaseGridContainer from '../../../src/grid/Container.mjs';
|
353
|
+
import Button from '../../../src/button/Base.mjs';
|
354
|
+
import MainStore from './MainStore.mjs';
|
355
|
+
|
356
|
+
class GridContainer extends BaseGridContainer {
|
357
|
+
static config = {
|
358
|
+
className: 'Neo.examples.grid.bigData.GridContainer',
|
359
|
+
// ... other grid-specific configurations
|
360
|
+
store: MainStore // The grid declaratively specifies which store to use
|
361
|
+
}
|
362
|
+
// ...
|
363
|
+
}
|
364
|
+
|
365
|
+
export default Neo.setupClass(GridContainer);
|
366
|
+
```
|
367
|
+
|
368
|
+
By simply assigning `MainStore` to the `store` config property, the `GridContainer` instance will automatically use the data
|
369
|
+
provided and managed by `MainStore`. This demonstrates the seamless integration of non-DOM data logic with DOM-rendering
|
370
|
+
components through a consistent declarative config system.
|
371
|
+
|
372
|
+
**Note**: This section focuses on how grid's `store config property integrates with the class config system. A detailed
|
373
|
+
explanation of all grid features, column configurations, and data handling methods will be covered in a
|
374
|
+
separate Grid Component guide.
|
375
|
+
|
376
|
+
### 4. Configuring Controllers and State Providers for Application Logic
|
377
|
+
|
378
|
+
Neo.mjs's declarative configuration extends to application logic components such as controllers and state providers,
|
379
|
+
allowing you to define their association and initial properties directly within the `static config` of your views or other classes.
|
380
|
+
This offers significant flexibility in how these non-DOM entities are managed.
|
381
|
+
|
382
|
+
**Important Best Practice**:
|
383
|
+
|
384
|
+
It's a core Neo.mjs best practice that controllers and state providers are typically associated with non-leaf (container)
|
385
|
+
components, such as `Viewport`s or other top-level application containers, rather than individual leaf-node UI components
|
386
|
+
(like a `Button` or `Label`). This promotes a clear separation of concerns, keeping complex logic centralized and allowing
|
387
|
+
leaf components to remain focused on their rendering responsibilities. A `Viewport`, for example, often manages overall
|
388
|
+
application state, routing, and user interactions, making it a natural home for a controller and state provider.
|
389
|
+
|
390
|
+
Consider the `Portal.view.Viewport` class
|
391
|
+
[[Source: Viewport.mjs](https://github.com/neomjs/neo/blob/dev/apps/portal/view/Viewport.mjs)],
|
392
|
+
which effectively utilizes both a controller and a state provider for application-level concerns:
|
393
|
+
|
394
|
+
```javascript readonly
|
395
|
+
import ViewportController from './ViewportController.mjs';
|
396
|
+
import ViewportStateProvider from './ViewportStateProvider.mjs';
|
397
|
+
|
398
|
+
class Viewport extends BaseViewport {
|
399
|
+
static config = {
|
400
|
+
className : 'Portal.view.Viewport',
|
401
|
+
controller : ViewportController, // Option 1: Direct module reference
|
402
|
+
// ... layout, items
|
403
|
+
stateProvider: ViewportStateProvider // Option 1: Direct module reference
|
404
|
+
}
|
405
|
+
// ... rest of the class
|
406
|
+
}
|
407
|
+
```
|
408
|
+
|
409
|
+
Neo.mjs provides several ways to configure these entities within the `static config` block:
|
410
|
+
|
411
|
+
1. **Direct Module Reference (Most Common)**</br>
|
412
|
+
You can directly provide the imported class module to the controller or stateProvider config.
|
413
|
+
Neo.mjs will automatically create an instance of that class and assign it to the property.
|
414
|
+
This is the simplest and most common approach, as seen in the Viewport example above.
|
415
|
+
|
416
|
+
```javascript readonly
|
417
|
+
// In MyContainer.mjs (a non-leaf component)
|
418
|
+
import MyController from './MyController.mjs';
|
419
|
+
|
420
|
+
class MyContainer extends Container {
|
421
|
+
static config = {
|
422
|
+
controller: MyController // Neo.mjs will instantiate MyController
|
423
|
+
}
|
424
|
+
}
|
425
|
+
```
|
426
|
+
|
427
|
+
2. **Config Object with `module`**</br>
|
428
|
+
If you need to pass additional configuration properties to the controller or state provider at the time of its creation,
|
429
|
+
you can provide a config object that includes the `module` property along with any other desired properties.
|
430
|
+
Neo.mjs will use this full config object to create the instance.
|
431
|
+
|
432
|
+
```javascript readonly
|
433
|
+
// In MyContainer.mjs
|
434
|
+
import MyController from './MyController.mjs';
|
435
|
+
|
436
|
+
class MyContainer extends Container {
|
437
|
+
static config = {
|
438
|
+
controller: {
|
439
|
+
module : MyController,
|
440
|
+
myCustomProperty: 'initialValue',
|
441
|
+
anotherSetting : true
|
442
|
+
}
|
443
|
+
}
|
444
|
+
}
|
445
|
+
```
|
446
|
+
|
447
|
+
3. **Inline Class Definition (for tightly coupled cases)** </br>
|
448
|
+
For very specific or small controllers/state providers that are tightly coupled to a single component and don't
|
449
|
+
require external reusability, you can define them inline as a nested configuration object within the component's config.
|
450
|
+
While this reduces external reusability, it keeps highly related code together and maintains the declarative paradigm.
|
451
|
+
|
452
|
+
```javascript readonly
|
453
|
+
// In MyContainer.mjs
|
454
|
+
class MyContainer extends Container {
|
455
|
+
static config = {
|
456
|
+
stateProvider: {
|
457
|
+
data: {
|
458
|
+
foo: 'bar'
|
459
|
+
}
|
460
|
+
}
|
461
|
+
}
|
462
|
+
}
|
463
|
+
```
|
464
|
+
|
465
|
+
This approach allows you to fully define the state provider's behavior and dependencies directly within the container's configuration.
|
466
|
+
|
467
|
+
These options provide a powerful and consistent way to inject behavior and state management into your components,
|
468
|
+
all within the unified class config system, further demonstrating its versatility for both UI and non-UI logic.
|
469
|
+
|
470
|
+
**Note**: This section focuses on the configuration methods for controllers and state providers. Detailed explanations of
|
471
|
+
controller methods, state management patterns, and specific API functionalities will be covered in dedicated guides on
|
472
|
+
Controllers and State Management.
|
473
|
+
|
474
|
+
### 5. Non-DOM Entity Configuration: Routing with routes
|
475
|
+
|
476
|
+
Neo.mjs enables declarative routing directly within the static config of your controllers (or other relevant classes), often a ViewportController for top-level application navigation. This allows you to define URL hash patterns and map them to specific controller methods, creating a clear, maintainable routing system.
|
477
|
+
|
478
|
+
Consider the ViewportController.mjs
|
479
|
+
[[Source: ViewportController.mjs](https://github.com/neomjs/neo/blob/dev/apps/portal/view/ViewportController.mjs)]:
|
480
|
+
|
481
|
+
```javascript readonly
|
482
|
+
// From: Portal.view.ViewportController
|
483
|
+
import Controller from '../../../src/controller/Component.mjs';
|
484
|
+
// ... other imports
|
485
|
+
|
486
|
+
class ViewportController extends Controller {
|
487
|
+
static config = {
|
488
|
+
className: 'Portal.view.ViewportController',
|
489
|
+
ntype : 'viewport-controller',
|
490
|
+
// ... other configurations
|
491
|
+
/**
|
492
|
+
* @member {Object} routes
|
493
|
+
*/
|
494
|
+
routes: {
|
495
|
+
'/about-us' : 'onAboutUsRoute',
|
496
|
+
'/blog' : 'onBlogRoute',
|
497
|
+
'/docs' : 'onDocsRoute',
|
498
|
+
'/examples' : 'onExamplesRoute',
|
499
|
+
'/examples/{itemId}': 'onExamplesRoute',
|
500
|
+
'/home' : 'onHomeRoute',
|
501
|
+
'/learn' : 'onLearnRoute',
|
502
|
+
'/learn/{itemId}' : 'onLearnRoute',
|
503
|
+
'/services' : 'onServicesRoute'
|
504
|
+
},
|
505
|
+
// ... other configurations
|
506
|
+
}
|
507
|
+
// ... controller methods like onAboutUsRoute, onBlogRoute, etc.
|
508
|
+
}
|
509
|
+
```
|
510
|
+
|
511
|
+
In this `ViewportController`'s `static config`:
|
512
|
+
|
513
|
+
* The `routes` object maps URL hash patterns (keys) to controller method names (values).
|
514
|
+
* For example, when the browser's hash changes to `#/home`, the `onHomeRoute` method within `ViewportController` is automatically invoked.
|
515
|
+
* Routes can include dynamic parameters using curly braces, like `/examples/{itemId}`. When such a route is matched
|
516
|
+
(e.g., `#/examples/my-component`), the `onExamplesRoute` method will be called, and the `itemId` value (`my-component`)
|
517
|
+
will be passed as a parameter to the method.
|
518
|
+
* This declarative setup centralizes your application's routing logic, making it easy to understand the application's
|
519
|
+
navigation paths and their corresponding actions at a glance.
|
520
|
+
* The controller methods, such as `onHomeRoute`, then typically manage the application's UI state based on the route,
|
521
|
+
for instance, by setting the `activeIndex` of a card layout container to display the correct view:
|
522
|
+
|
523
|
+
```javascript readonly
|
524
|
+
// From: Portal.view.ViewportController
|
525
|
+
// ...
|
526
|
+
/**
|
527
|
+
* @param {Object} params
|
528
|
+
* @param {Object} value
|
529
|
+
* @param {Object} oldValue
|
530
|
+
*/
|
531
|
+
onHomeRoute(params, value, oldValue) {
|
532
|
+
this.setMainContentIndex(0)
|
533
|
+
}
|
534
|
+
|
535
|
+
/**
|
536
|
+
* @param {Number} index
|
537
|
+
*/
|
538
|
+
async setMainContentIndex(index) {
|
539
|
+
let me = this,
|
540
|
+
{activeIndex, mainContentLayout} = me,
|
541
|
+
container = me.getReference('main-content');
|
542
|
+
// ... logic to update the main content container's activeIndex
|
543
|
+
if (index !== activeIndex) {
|
544
|
+
me.activeIndex = index;
|
545
|
+
// ... more complex layout transition logic
|
546
|
+
container.layout.activeIndex = index;
|
547
|
+
}
|
548
|
+
}
|
549
|
+
```
|
550
|
+
|
551
|
+
This demonstrates a complete declarative flow: the `static config` defines the routes, which then trigger methods within
|
552
|
+
the same controller to update the UI, all within a consistent declarative pattern.
|
553
|
+
|
554
|
+
**Note**: This section focuses on how routes are defined and linked to controller methods through the `static config`.
|
555
|
+
A comprehensive guide on Neo.mjs routing, including advanced features like route guards, nested routes, and router
|
556
|
+
history management, will be provided in a separate, dedicated guide.
|
557
|
+
|
558
|
+
## Conclusion: Empowering Declarative Development with Neo.mjs Configs
|
559
|
+
|
560
|
+
The Neo.mjs Unified Class Config System is a cornerstone of the framework's design, extending the power of declarative
|
561
|
+
programming beyond traditional UI elements to encompass every aspect of your application. By providing a consistent,
|
562
|
+
intuitive, and hierarchical way to define and manage properties for all classes — from UI components to data stores,
|
563
|
+
controllers, and routers—Neo.mjs significantly reduces cognitive load and enhances maintainability.
|
564
|
+
|
565
|
+
This system empowers developers to describe the desired state and behavior of their applications in a clear, concise,
|
566
|
+
and unified manner. The inherent reactivity of configs, coupled with powerful lifecycle hooks like beforeGet, beforeSet,
|
567
|
+
and afterSet, offers unparalleled control and flexibility, enabling dynamic updates and sophisticated data flow management.
|
568
|
+
|
569
|
+
Ultimately, the Neo.mjs config system streamlines application development, making it easier to build complex,
|
570
|
+
high-performance, and maintainable web applications. It's a testament to Neo.mjs's commitment to providing a truly
|
571
|
+
unified and developer-friendly experience.
|
572
|
+
|
573
|
+
For a detailed understanding of the internal mechanics, advanced usage patterns, and the full power of config lifecycle
|
574
|
+
hooks, please refer to the [Config System Deep Dive guide](#/learn/guides.ConfigSystemDeepDive).
|
package/learn/benefits/Effort.md
CHANGED
@@ -1,3 +1,48 @@
|
|
1
|
-
WIth Neo.mjs applications, the relationship between complexity and effort is nearly linear. In a typical frameworks, trivial applications are simple to implement, but complex applications can be very expensive and difficult to implement.
|
2
1
|
|
3
|
-
|
2
|
+
## A Strategic Investment
|
3
|
+
|
4
|
+
In the world of web development, the relationship between application complexity and the effort required to build and
|
5
|
+
maintain it is rarely linear. As the chart below illustrates, traditional JavaScript frameworks and even vanilla JavaScript
|
6
|
+
often lead to an exponential increase in effort as applications grow in features, data, and user interactions.
|
7
|
+
|
8
|
+
<img width="85%" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/website/learn/ComplexityAndEffort.png"></img>
|
9
|
+
|
10
|
+
### The Neo.mjs Advantage: A Linear Path to Scalability
|
11
|
+
|
12
|
+
Neo.mjs presents a fundamentally different curve. While there might be a slightly higher initial learning curve or setup
|
13
|
+
effort compared to starting with a trivial vanilla JavaScript website, this upfront investment is a strategic advantage.
|
14
|
+
Developers typically get "up to speed" with Neo.mjs within about a week, grasping its core concepts and unique architecture.
|
15
|
+
|
16
|
+
This initial effort is quickly recouped, leading to a significant "turning point" where developer productivity skyrockets.
|
17
|
+
Once familiar with the Neo.mjs paradigm, the effort required to implement increasingly complex features scales almost
|
18
|
+
linearly. This is a stark contrast to other frameworks where complexity often translates into spiraling costs,
|
19
|
+
diminishing returns, and a constant battle against technical debt.
|
20
|
+
|
21
|
+
### Why Neo.mjs Maintains Linearity:
|
22
|
+
|
23
|
+
Neo.mjs achieves this remarkable linearity through a combination of architectural innovations and developer-centric design:
|
24
|
+
|
25
|
+
1. **Unified Config System**: All aspects of your application—from UI components and layouts to data models, controllers,
|
26
|
+
and routing—are defined declaratively through a consistent, hierarchical configuration. This eliminates the need to learn
|
27
|
+
disparate configuration paradigms, significantly reducing cognitive load and development time as complexity increases.
|
28
|
+
|
29
|
+
2. **Off-Main-Thread Architecture (OMT)**: By offloading business logic and data processing to Web Workers, Neo.mjs ensures
|
30
|
+
the main thread remains responsive, even in highly complex, data-intensive applications. This inherent separation of
|
31
|
+
concerns prevents UI freezes and simplifies debugging, as performance bottlenecks are isolated and easier to identify.
|
32
|
+
The multi-threaded nature also inherently supports technical scaling by distributing computational load across CPU cores.
|
33
|
+
|
34
|
+
3. **Predictable Component Lifecycle**: The well-defined and consistent lifecycle of Neo.mjs components, from creation
|
35
|
+
to destruction, provides clear hooks for managing state and resources. This predictability reduces the likelihood of
|
36
|
+
memory leaks and unexpected behavior, making complex applications more stable and easier to maintain.
|
37
|
+
|
38
|
+
4. **Modular and Scalable Design**: Neo.mjs encourages a modular approach, allowing developers to build complex
|
39
|
+
applications from smaller, self-contained, and reusable components. This design philosophy, combined with features like
|
40
|
+
intelligent lazy loading, ensures that even massive enterprise applications remain manageable and performant.
|
41
|
+
|
42
|
+
5. **Reduced Build Tooling Overhead**: The zero-build development mode and native ES module support minimize reliance
|
43
|
+
on complex build configurations and transpilation steps. This accelerates the development feedback loop, allowing
|
44
|
+
developers to iterate faster and focus on writing application logic rather than wrestling with tooling.
|
45
|
+
|
46
|
+
This linear relationship translates directly into tangible benefits: faster development cycles, lower maintenance costs,
|
47
|
+
and a more predictable path to scaling your applications, regardless of their eventual complexity. For decision-makers,
|
48
|
+
this means a more efficient use of development resources and a higher return on investment for web application projects.
|