neo.mjs 6.13.0 → 6.14.0
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/apps/covid/neo-config.json +1 -1
- package/apps/portal/view/ViewportController.mjs +5 -4
- package/apps/portal/view/home/ContentBox.mjs +80 -0
- package/apps/portal/view/home/MainContainer.mjs +51 -15
- package/apps/portal/view/learn/ContentTreeList.mjs +10 -3
- package/apps/portal/view/learn/MainContainerController.mjs +37 -5
- package/apps/portal/view/learn/MainContainerModel.mjs +51 -7
- package/apps/portal/view/learn/PageContainer.mjs +21 -9
- package/apps/sharedcovid/neo-config.json +1 -1
- package/examples/form/field/select/MainContainer.mjs +1 -1
- package/package.json +3 -3
- package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +16 -1
- package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +180 -0
- package/resources/data/deck/learnneo/pages/Config.md +11 -4
- package/resources/data/deck/learnneo/pages/DescribingTheUI.md +6 -0
- package/resources/data/deck/learnneo/pages/Earthquakes.md +36 -9
- package/resources/data/deck/learnneo/pages/Events.md +55 -43
- package/resources/data/deck/learnneo/pages/GuideEvents.md +9 -8
- package/resources/data/deck/learnneo/pages/References.md +14 -7
- package/resources/data/deck/learnneo/pages/TodoList.md +241 -0
- package/resources/data/deck/learnneo/pages/WhyNeo-Quick.md +6 -11
- package/resources/data/deck/learnneo/tree.json +2 -0
- package/resources/scss/src/apps/portal/home/ContentBox.scss +26 -0
- package/resources/scss/src/apps/portal/home/MainContainer.scss +4 -12
- package/resources/scss/src/apps/portal/learn/MainContainer.scss +0 -7
- package/resources/scss/src/apps/portal/learn/PageContainer.scss +35 -0
- package/resources/scss/src/apps/portal/learn/PageSectionsPanel.scss +8 -0
- package/src/component/Base.mjs +16 -1
- package/src/controller/Application.mjs +12 -1
- package/src/controller/Base.mjs +15 -4
- package/src/controller/Component.mjs +5 -1
- package/src/core/Observable.mjs +62 -22
- package/src/form/field/Base.mjs +21 -9
- package/src/form/field/Select.mjs +166 -117
- package/src/form/field/Text.mjs +7 -10
- package/src/main/DomEvents.mjs +2 -1
- package/src/main/addon/MonacoEditor.mjs +2 -2
- package/src/main/addon/Navigator.mjs +27 -24
- package/src/selection/ListModel.mjs +13 -14
- package/src/selection/Model.mjs +10 -10
- package/src/util/HashHistory.mjs +1 -0
- package/src/worker/App.mjs +5 -1
- package/src/worker/Manager.mjs +14 -7
- package/test/components/files/form/field/Select.mjs +6 -4
@@ -3,6 +3,6 @@
|
|
3
3
|
"basePath" : "../../",
|
4
4
|
"environment" : "development",
|
5
5
|
"mainPath" : "./Main.mjs",
|
6
|
-
"mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Stylesheet"],
|
6
|
+
"mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Navigator", "Stylesheet"],
|
7
7
|
"themes" : ["neo-theme-dark", "neo-theme-light"]
|
8
8
|
}
|
@@ -19,10 +19,11 @@ class ViewportController extends Controller {
|
|
19
19
|
* @member {Object} routes
|
20
20
|
*/
|
21
21
|
routes: {
|
22
|
-
'/blog'
|
23
|
-
'/docs'
|
24
|
-
'/home'
|
25
|
-
'/learn': 'onLearnRoute'
|
22
|
+
'/blog' : 'onBlogRoute',
|
23
|
+
'/docs' : 'onDocsRoute',
|
24
|
+
'/home' : 'onHomeRoute',
|
25
|
+
'/learn' : 'onLearnRoute',
|
26
|
+
'/learn/{itemId}': 'onLearnRoute'
|
26
27
|
}
|
27
28
|
}
|
28
29
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import Base from '../../../../src/component/Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Portal.view.home.ContentBox
|
5
|
+
* @extends Neo.component.Base
|
6
|
+
*/
|
7
|
+
class ContentBox extends Base {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Portal.view.home.ContentBox'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Portal.view.home.ContentBox',
|
14
|
+
/**
|
15
|
+
* @member {String[]} baseCls=['portal-content-box']
|
16
|
+
* @protected
|
17
|
+
*/
|
18
|
+
baseCls: ['portal-content-box'],
|
19
|
+
/**
|
20
|
+
* @member {String[]|null} content_=null
|
21
|
+
*/
|
22
|
+
content_: null,
|
23
|
+
/**
|
24
|
+
* @member {String|null} header_=null
|
25
|
+
*/
|
26
|
+
header_: null,
|
27
|
+
/**
|
28
|
+
* @member {String|null} route_=null
|
29
|
+
*/
|
30
|
+
route_: null,
|
31
|
+
/**
|
32
|
+
* @member {Object} _vdom
|
33
|
+
*/
|
34
|
+
_vdom:
|
35
|
+
{tag: 'a', cn: [
|
36
|
+
{tag: 'h3', cls: ['portal-content-box-headline']},
|
37
|
+
{tag: 'ul', cls: ['portal-content-box-content'], cn: []}
|
38
|
+
]}
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Triggered after the content config got changed
|
43
|
+
* @param {String[]|null} value
|
44
|
+
* @param {String[]|null} oldValue
|
45
|
+
* @protected
|
46
|
+
*/
|
47
|
+
afterSetContent(value, oldValue) {
|
48
|
+
value?.forEach(item => {
|
49
|
+
this.vdom.cn[1].cn.push({tag: 'li', html: item})
|
50
|
+
})
|
51
|
+
|
52
|
+
this.update()
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Triggered after the header config got changed
|
57
|
+
* @param {String|null} value
|
58
|
+
* @param {String|null} oldValue
|
59
|
+
* @protected
|
60
|
+
*/
|
61
|
+
afterSetHeader(value, oldValue) {
|
62
|
+
this.vdom.cn[0].html = value;
|
63
|
+
this.update()
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Triggered after the route config got changed
|
68
|
+
* @param {String|null} value
|
69
|
+
* @param {String|null} oldValue
|
70
|
+
* @protected
|
71
|
+
*/
|
72
|
+
afterSetRoute(value, oldValue) {
|
73
|
+
this.vdom.href = value;
|
74
|
+
this.update()
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
Neo.setupClass(ContentBox);
|
79
|
+
|
80
|
+
export default ContentBox;
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import Button
|
2
|
-
import Container
|
1
|
+
import Button from '../../../../src/button/Base.mjs';
|
2
|
+
import Container from '../../../../src/container/Base.mjs';
|
3
|
+
import ContentBox from './ContentBox.mjs';
|
3
4
|
|
4
5
|
/**
|
5
6
|
* @class Portal.view.home.MainContainer
|
@@ -16,13 +17,16 @@ class Viewport extends Container {
|
|
16
17
|
* @member {String[]} cls=['newwebsite-viewport']
|
17
18
|
*/
|
18
19
|
cls: ['newwebsite-viewport'],
|
20
|
+
/**
|
21
|
+
* @member {Object} layout={ntype:'vbox',align:'center',pack:'center'}
|
22
|
+
*/
|
23
|
+
layout: {ntype: 'vbox', align: 'center', pack: 'center'},
|
19
24
|
/**
|
20
25
|
* @member {Object[]} items
|
21
26
|
*/
|
22
27
|
items: [{
|
23
|
-
|
24
|
-
|
25
|
-
flex : 'none'
|
28
|
+
cls : ['vector'],
|
29
|
+
flex : 'none'
|
26
30
|
}, {
|
27
31
|
cls : 'neo-h1',
|
28
32
|
flex: 'none',
|
@@ -31,23 +35,55 @@ class Viewport extends Container {
|
|
31
35
|
module: Container,
|
32
36
|
cls : ['button-group'],
|
33
37
|
flex : 'none',
|
38
|
+
layout: {ntype: 'hbox'},
|
34
39
|
|
35
40
|
items: [{
|
36
|
-
module : Button,
|
37
|
-
cls : 'get-started-button',
|
38
|
-
text : 'Get started',
|
39
|
-
flex : 'none',
|
40
|
-
tooltip: {
|
41
|
-
text : 'Coming soon',
|
42
|
-
showDelay: 0,
|
43
|
-
hideDelay: 0
|
44
|
-
}
|
45
|
-
}, {
|
46
41
|
module: Button,
|
47
42
|
flex : 'none',
|
48
43
|
text : 'View on GitHub',
|
49
44
|
ui : 'secondary',
|
50
45
|
url : 'https://github.com/neomjs/neo'
|
46
|
+
}, {
|
47
|
+
module: Button,
|
48
|
+
flex : 'none',
|
49
|
+
text : 'Get started',
|
50
|
+
route : '/learn'
|
51
|
+
}]
|
52
|
+
}, {
|
53
|
+
module: Container,
|
54
|
+
flex : '1 0 auto',
|
55
|
+
layout: {ntype: 'hbox', align: 'stretch'},
|
56
|
+
|
57
|
+
items: [{
|
58
|
+
module: ContentBox,
|
59
|
+
header: 'Quick Application Development',
|
60
|
+
route : '#/learn/WhyNeo-Quick',
|
61
|
+
|
62
|
+
content: [
|
63
|
+
'Property lifecycle hooks',
|
64
|
+
'Elegant state management',
|
65
|
+
'Simple and powerful debugging'
|
66
|
+
]
|
67
|
+
}, {
|
68
|
+
module: ContentBox,
|
69
|
+
header: 'Extreme Speed',
|
70
|
+
route : '#/learn/WhyNeo-Speed',
|
71
|
+
|
72
|
+
content: [
|
73
|
+
'Item 1',
|
74
|
+
'Item 2',
|
75
|
+
'Item 3'
|
76
|
+
]
|
77
|
+
}, {
|
78
|
+
module: ContentBox,
|
79
|
+
header: 'Multi Window Apps',
|
80
|
+
route : '#/learn/WhyNeo-Multi-Window',
|
81
|
+
|
82
|
+
content: [
|
83
|
+
'Item 1',
|
84
|
+
'Item 2',
|
85
|
+
'Item 3'
|
86
|
+
]
|
51
87
|
}]
|
52
88
|
}]
|
53
89
|
}
|
@@ -51,8 +51,11 @@ class ContentTreeList extends TreeList {
|
|
51
51
|
* @param {Object} value
|
52
52
|
* @param {Object} oldValue
|
53
53
|
*/
|
54
|
-
afterSetCurrentPageRecord(value, oldValue) {
|
55
|
-
|
54
|
+
async afterSetCurrentPageRecord(value, oldValue) {
|
55
|
+
if (value) {
|
56
|
+
await this.timeout(20);
|
57
|
+
this.selectionModel.select(value)
|
58
|
+
}
|
56
59
|
}
|
57
60
|
|
58
61
|
/**
|
@@ -77,7 +80,11 @@ class ContentTreeList extends TreeList {
|
|
77
80
|
*/
|
78
81
|
onLeafItemClick(record) {
|
79
82
|
super.onLeafItemClick(record);
|
80
|
-
|
83
|
+
|
84
|
+
Neo.Main.setRoute({
|
85
|
+
value : `/learn/${record.id}`,
|
86
|
+
windowId: this.windowId
|
87
|
+
})
|
81
88
|
}
|
82
89
|
}
|
83
90
|
|
@@ -10,7 +10,13 @@ class MainContainerController extends Controller {
|
|
10
10
|
* @member {String} className='Portal.view.learn.MainContainerController'
|
11
11
|
* @protected
|
12
12
|
*/
|
13
|
-
className: 'Portal.view.learn.MainContainerController'
|
13
|
+
className: 'Portal.view.learn.MainContainerController',
|
14
|
+
/**
|
15
|
+
* @member {Object} routes
|
16
|
+
*/
|
17
|
+
routes: {
|
18
|
+
'/learn/{itemId}': 'onRouteLearnItem'
|
19
|
+
}
|
14
20
|
}
|
15
21
|
|
16
22
|
/**
|
@@ -48,6 +54,16 @@ class MainContainerController extends Controller {
|
|
48
54
|
return searchString ? JSON.parse(`{"${decodeURI(searchString.replace(/&/g, "\",\"").replace(/=/g, "\":\""))}"}`) : {}
|
49
55
|
}
|
50
56
|
|
57
|
+
/**
|
58
|
+
* @param {String} learnItem
|
59
|
+
*/
|
60
|
+
navigateTo(learnItem) {
|
61
|
+
Neo.Main.setRoute({
|
62
|
+
value : `/learn/${learnItem}`,
|
63
|
+
windowId: this.component.windowId
|
64
|
+
})
|
65
|
+
}
|
66
|
+
|
51
67
|
/**
|
52
68
|
* @param {Object} data
|
53
69
|
* @param {String} data.appName
|
@@ -177,16 +193,32 @@ class MainContainerController extends Controller {
|
|
177
193
|
* @param {Object} data
|
178
194
|
*/
|
179
195
|
onNextPageButtonClick(data) {
|
180
|
-
|
181
|
-
model.setData('currentPageRecord', model.getData('nextPageRecord'))
|
196
|
+
this.navigateTo(this.getModel().getData('nextPageRecord').id)
|
182
197
|
}
|
183
198
|
|
184
199
|
/**
|
185
200
|
* @param {Object} data
|
186
201
|
*/
|
187
202
|
onPreviousPageButtonClick(data) {
|
188
|
-
|
189
|
-
|
203
|
+
this.navigateTo(this.getModel().getData('previousPageRecord').id)
|
204
|
+
}
|
205
|
+
|
206
|
+
/**
|
207
|
+
* @param {Object} data
|
208
|
+
*/
|
209
|
+
onRouteLearnItem(data) {
|
210
|
+
let model = this.getModel(),
|
211
|
+
store = model.getStore('contentTree');
|
212
|
+
|
213
|
+
if (store.getCount() > 0) {
|
214
|
+
model.data.currentPageRecord = store.get(data.itemId)
|
215
|
+
} else {
|
216
|
+
store.on({
|
217
|
+
load : () => {model.data.currentPageRecord = store.get(data.itemId)},
|
218
|
+
delay: 10,
|
219
|
+
once : true
|
220
|
+
})
|
221
|
+
}
|
190
222
|
}
|
191
223
|
}
|
192
224
|
|
@@ -40,14 +40,19 @@ class MainContainerModel extends Component {
|
|
40
40
|
deck: null,
|
41
41
|
/**
|
42
42
|
* The record which gets shown as the content page
|
43
|
-
* @member {Object} data.
|
43
|
+
* @member {Object} data.nextPageRecord=null
|
44
44
|
*/
|
45
45
|
nextPageRecord: null,
|
46
46
|
/**
|
47
47
|
* The record which gets shown as the content page
|
48
|
-
* @member {Object} data.
|
48
|
+
* @member {Object} data.previousPageRecord=null
|
49
49
|
*/
|
50
|
-
previousPageRecord: null
|
50
|
+
previousPageRecord: null,
|
51
|
+
/**
|
52
|
+
* Merging the direct parent text
|
53
|
+
* @member {String|null} data.previousPageText=null
|
54
|
+
*/
|
55
|
+
previousPageText: null
|
51
56
|
},
|
52
57
|
/**
|
53
58
|
* @member {Object} stores
|
@@ -62,6 +67,22 @@ class MainContainerModel extends Component {
|
|
62
67
|
}
|
63
68
|
}
|
64
69
|
|
70
|
+
/**
|
71
|
+
* Combines the record parent node name (if available) with the record name
|
72
|
+
* @param {Object} record
|
73
|
+
* @param {Neo.data.Store} store
|
74
|
+
* @returns {String|null}
|
75
|
+
*/
|
76
|
+
getRecordTreeName(record, store) {
|
77
|
+
let parentText = record.name;
|
78
|
+
|
79
|
+
if (record.parentId !== null) {
|
80
|
+
parentText = store.get(record.parentId).name + ': ' + parentText
|
81
|
+
}
|
82
|
+
|
83
|
+
return parentText
|
84
|
+
}
|
85
|
+
|
65
86
|
/**
|
66
87
|
* @param {String} key
|
67
88
|
* @param {*} value
|
@@ -79,32 +100,36 @@ class MainContainerModel extends Component {
|
|
79
100
|
store = me.getStore('contentTree'),
|
80
101
|
index = store.indexOf(value),
|
81
102
|
nextPageRecord = null,
|
103
|
+
nextPageText = null,
|
82
104
|
previousPageRecord = null,
|
105
|
+
previousPageText = null,
|
83
106
|
i, record;
|
84
107
|
|
85
108
|
// the logic assumes that the tree store is sorted
|
86
109
|
for (i=index-1; i >= 0; i--) {
|
87
110
|
record = store.getAt(i);
|
88
111
|
|
89
|
-
if (record.isLeaf) {
|
112
|
+
if (record.isLeaf && !me.recordIsHidden(record, store)) {
|
90
113
|
previousPageRecord = record;
|
114
|
+
previousPageText = me.getRecordTreeName(record, store);
|
91
115
|
break
|
92
116
|
}
|
93
117
|
}
|
94
118
|
|
95
|
-
me.setData({previousPageRecord});
|
119
|
+
me.setData({previousPageText, previousPageRecord});
|
96
120
|
|
97
121
|
// the logic assumes that the tree store is sorted
|
98
122
|
for (i=index+1; i < countPages; i++) {
|
99
123
|
record = store.getAt(i);
|
100
124
|
|
101
|
-
if (record.isLeaf) {
|
125
|
+
if (record.isLeaf && !me.recordIsHidden(record, store)) {
|
102
126
|
nextPageRecord = record;
|
127
|
+
nextPageText = me.getRecordTreeName(record, store);
|
103
128
|
break
|
104
129
|
}
|
105
130
|
}
|
106
131
|
|
107
|
-
me.setData({nextPageRecord});
|
132
|
+
me.setData({nextPageText, nextPageRecord});
|
108
133
|
|
109
134
|
break
|
110
135
|
}
|
@@ -118,6 +143,25 @@ class MainContainerModel extends Component {
|
|
118
143
|
}
|
119
144
|
}
|
120
145
|
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
* We need to check the parent-node chain inside the tree.
|
149
|
+
* => Any hidden parent-node results in a hidden record.
|
150
|
+
* @param {Object} record
|
151
|
+
* @param {Neo.data.Store} store
|
152
|
+
* @returns {Boolean}
|
153
|
+
*/
|
154
|
+
recordIsHidden(record, store) {
|
155
|
+
if (record.hidden) {
|
156
|
+
return true
|
157
|
+
}
|
158
|
+
|
159
|
+
if (record.parentId !== null) {
|
160
|
+
return this.recordIsHidden(store.get(record.parentId), store)
|
161
|
+
}
|
162
|
+
|
163
|
+
return false
|
164
|
+
}
|
121
165
|
}
|
122
166
|
|
123
167
|
Neo.setupClass(MainContainerModel);
|
@@ -38,14 +38,22 @@ class PageContainer extends Container {
|
|
38
38
|
}, {
|
39
39
|
module: Toolbar,
|
40
40
|
flex : 'none',
|
41
|
+
cls : 'content-bottom-toolbar',
|
41
42
|
items : [{
|
43
|
+
cls : ['content-bottom-toolbar-previous'],
|
42
44
|
handler : 'onPreviousPageButtonClick',
|
43
45
|
hidden : true,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
iconCls : 'fa fa-chevron-left',
|
47
|
+
reference: 'prev-page-button',
|
48
|
+
ui : 'secondary'
|
49
|
+
}, {
|
50
|
+
cls : ['content-bottom-toolbar-next'],
|
51
|
+
handler : 'onNextPageButtonClick',
|
52
|
+
hidden : true,
|
53
|
+
iconCls : 'fa fa-chevron-right',
|
54
|
+
iconPosition: 'right',
|
55
|
+
reference : 'next-page-button',
|
56
|
+
ui : 'secondary'
|
49
57
|
}]
|
50
58
|
}],
|
51
59
|
/**
|
@@ -81,10 +89,12 @@ class PageContainer extends Container {
|
|
81
89
|
*/
|
82
90
|
afterSetNextPageRecord(value, oldValue) {
|
83
91
|
if (oldValue !== undefined) {
|
92
|
+
let me = this;
|
93
|
+
|
84
94
|
if (value) {
|
85
|
-
|
95
|
+
me.nextPageButton.set({hidden: false, text: me.getModel().getData('nextPageText')})
|
86
96
|
} else {
|
87
|
-
|
97
|
+
me.nextPageButton.hidden = true
|
88
98
|
}
|
89
99
|
}
|
90
100
|
}
|
@@ -96,10 +106,12 @@ class PageContainer extends Container {
|
|
96
106
|
*/
|
97
107
|
afterSetPreviousPageRecord(value, oldValue) {
|
98
108
|
if (oldValue !== undefined) {
|
109
|
+
let me = this;
|
110
|
+
|
99
111
|
if (value) {
|
100
|
-
|
112
|
+
me.prevPageButton.set({hidden: false, text: me.getModel().getData('previousPageText')})
|
101
113
|
} else {
|
102
|
-
|
114
|
+
me.prevPageButton.hidden = true
|
103
115
|
}
|
104
116
|
}
|
105
117
|
}
|
@@ -3,7 +3,7 @@
|
|
3
3
|
"basePath" : "../../",
|
4
4
|
"environment" : "development",
|
5
5
|
"mainPath" : "./Main.mjs",
|
6
|
-
"mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Stylesheet"],
|
6
|
+
"mainThreadAddons": ["AmCharts", "DragDrop", "MapboxGL", "Navigator", "Stylesheet"],
|
7
7
|
"themes" : ["neo-theme-dark", "neo-theme-light"],
|
8
8
|
"useSharedWorkers": true
|
9
9
|
}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
{
|
3
3
|
"name": "neo.mjs",
|
4
|
-
"version": "6.
|
4
|
+
"version": "6.14.0",
|
5
5
|
"description": "The webworkers driven UI framework",
|
6
6
|
"type": "module",
|
7
7
|
"repository": {
|
@@ -53,13 +53,13 @@
|
|
53
53
|
"envinfo": "^7.11.1",
|
54
54
|
"fs-extra": "^11.2.0",
|
55
55
|
"highlightjs-line-numbers.js": "^2.8.0",
|
56
|
-
"inquirer": "^9.2.
|
56
|
+
"inquirer": "^9.2.16",
|
57
57
|
"marked": "^12.0.1",
|
58
58
|
"monaco-editor": "^0.47.0",
|
59
59
|
"neo-jsdoc": "1.0.1",
|
60
60
|
"neo-jsdoc-x": "1.0.5",
|
61
61
|
"postcss": "^8.4.35",
|
62
|
-
"sass": "^1.
|
62
|
+
"sass": "^1.72.0",
|
63
63
|
"siesta-lite": "5.5.2",
|
64
64
|
"url": "^0.11.3",
|
65
65
|
"webpack": "^5.90.3",
|
@@ -1,3 +1,5 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
1
3
|
The purpose of this tutorial is to let you see the structure of a Neo.mjs workspace,
|
2
4
|
and the structure of an application.
|
3
5
|
|
@@ -5,7 +7,12 @@ If you wish, you can create a workspace by following the directions in the previ
|
|
5
7
|
|
6
8
|
<img src="https://raw.githubusercontent.com/neomjs/pages/main/resources/images/apps/learnneo/NeoWorkspace.png" style="height: 400px;">
|
7
9
|
|
8
|
-
As you can see, a Neo.mjs workspace is a conventional npm package.
|
10
|
+
As you can see, a Neo.mjs workspace is a conventional npm package.
|
11
|
+
|
12
|
+
|
13
|
+
## Browsing the workspace
|
14
|
+
|
15
|
+
If you run
|
9
16
|
the script `npm run server-start` from the workspace, Neo.mjs launches a web
|
10
17
|
server whose doc root is the workspace.
|
11
18
|
|
@@ -14,6 +21,8 @@ server whose doc root is the workspace.
|
|
14
21
|
If you drill down into the `src` directory you'll see your applications.
|
15
22
|
The `docs` directory holds the Neo.mjs API docs.
|
16
23
|
|
24
|
+
## The structure of a simple app
|
25
|
+
|
17
26
|
In order to discuss the structure of an app, we'll create a simple starter
|
18
27
|
app vis this script, run from the workspace. The script prompts for various
|
19
28
|
application settings.
|
@@ -29,6 +38,8 @@ These files are part of the typical structure of a Neo.mjs application. The file
|
|
29
38
|
You will, however, edit the main container, and its associated "controller" and "model",
|
30
39
|
as well as create new views classes, their controllers, and other application logic.
|
31
40
|
|
41
|
+
## Some source code
|
42
|
+
|
32
43
|
Now let's look at a source file. This is the contents of `MainView.mjs`.
|
33
44
|
|
34
45
|
<pre data-javascript>
|
@@ -61,6 +72,8 @@ This view also has a "controller" and "model". We'll talk about those later, but
|
|
61
72
|
a controller is where event handling and app logic goes, and a model is where you set up shared
|
62
73
|
bindable data.
|
63
74
|
|
75
|
+
## Adding a button to the view
|
76
|
+
|
64
77
|
We'll go into a lot more depth about view, controllers, and models in other topics, but to let
|
65
78
|
you see how a component is configured let's put a button in the container.
|
66
79
|
|
@@ -103,6 +116,8 @@ some value."
|
|
103
116
|
In another topic you'll learn about the Neo.mjs config system and declaratively describing views,
|
104
117
|
controllers, and other things.
|
105
118
|
|
119
|
+
## A running example
|
120
|
+
|
106
121
|
Here's a simplified running example. The `model` and `controller` are omitted, because they aren't
|
107
122
|
actually used in the example, and the import root path is different to reflect the location of the
|
108
123
|
Neo.mjs library relative to the examples.
|