neo.mjs 6.18.3 → 6.19.1
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/README.md +28 -214
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/colors/view/ViewportController.mjs +7 -3
- package/apps/portal/data/blog.json +13 -0
- package/apps/portal/view/HeaderToolbar.mjs +2 -2
- package/apps/portal/view/Viewport.mjs +4 -2
- package/apps/portal/view/ViewportController.mjs +88 -7
- package/apps/portal/view/blog/Container.mjs +8 -8
- package/apps/portal/view/blog/List.mjs +6 -6
- package/apps/portal/view/home/FooterContainer.mjs +123 -0
- package/apps/portal/view/home/MainContainer.mjs +3 -2
- package/apps/portal/view/home/parts/AfterMath.mjs +17 -24
- package/apps/portal/view/home/parts/Colors.mjs +2 -2
- package/apps/portal/view/home/parts/How.mjs +3 -3
- package/apps/portal/view/home/parts/MainNeo.mjs +6 -7
- package/apps/portal/view/home/parts/References.mjs +88 -0
- package/apps/portal/view/learn/MainContainer.mjs +3 -2
- package/apps/portal/view/learn/MainContainerController.mjs +11 -0
- package/apps/portal/view/learn/PageContainer.mjs +5 -3
- package/apps/portal/view/services/Component.mjs +73 -0
- package/apps/website/data/blog.json +13 -0
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/component/carousel/MainContainer.mjs +42 -33
- package/examples/layout/cube/MainContainer.mjs +217 -0
- package/examples/layout/cube/app.mjs +6 -0
- package/examples/layout/cube/index.html +11 -0
- package/examples/layout/cube/neo-config.json +6 -0
- package/examples/model/twoWay/MainContainer.mjs +76 -0
- package/examples/model/twoWay/app.mjs +6 -0
- package/examples/model/twoWay/index.html +11 -0
- package/examples/model/twoWay/neo-config.json +6 -0
- package/package.json +7 -7
- package/resources/data/deck/learnneo/pages/Earthquakes-01-goals.md +32 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-01-generate-a-workspace.md +47 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-02-generate-the-starter-app.md +150 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-03-debugging.md +136 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-04-fetch-data.md +146 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-05-refactor-the-table.md +146 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-06-use-a-view-model.md +301 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-07-use-the-google-maps-addon.md +175 -0
- package/resources/data/deck/learnneo/pages/Earthquakes-Lab-08-events.md +38 -0
- package/resources/data/deck/learnneo/pages/Earthquakes.md +8 -8
- package/resources/data/deck/learnneo/pages/Glossary.md +0 -0
- package/resources/data/deck/learnneo/pages/GuideEvents.md +80 -1
- package/resources/data/deck/learnneo/tree.json +2 -1
- package/resources/images/apps/portal/neo-references.png +0 -0
- package/resources/scss/src/apps/portal/Viewport.scss +18 -0
- package/resources/scss/src/apps/portal/blog/Container.scss +7 -7
- package/resources/scss/src/apps/portal/blog/List.scss +20 -16
- package/resources/scss/src/apps/portal/home/FooterContainer.scss +31 -0
- package/resources/scss/src/apps/portal/home/parts/AfterMath.scss +5 -0
- package/resources/scss/src/apps/portal/home/parts/MainNeo.scss +4 -5
- package/resources/scss/src/apps/portal/home/parts/References.scss +46 -0
- package/resources/scss/src/apps/portal/learn/ContentTreeList.scss +20 -0
- package/resources/scss/src/apps/portal/learn/ContentView.scss +4 -0
- package/resources/scss/src/apps/portal/learn/MainContainer.scss +1 -1
- package/resources/scss/src/apps/portal/learn/PageContainer.scss +22 -16
- package/resources/scss/src/apps/portal/services/Component.scss +20 -0
- package/resources/scss/src/component/Carousel.scss +21 -0
- package/resources/scss/src/examples/layout/cube/MainContainer.scss +7 -0
- package/resources/scss/src/layout/Cube.scss +80 -0
- package/resources/scss/src/tab/Container.scss +10 -10
- package/resources/scss/theme-neo-light/apps/portal/blog/Container.scss +3 -0
- package/resources/scss/theme-neo-light/form/field/Search.scss +1 -1
- package/resources/scss/theme-neo-light/tooltip/Base.scss +1 -1
- package/src/DefaultConfig.mjs +2 -2
- package/src/Main.mjs +15 -1
- package/src/Neo.mjs +14 -3
- package/src/calendar/view/MainContainer.mjs +8 -7
- package/src/component/Base.mjs +28 -8
- package/src/component/DateSelector.mjs +2 -2
- package/src/container/Base.mjs +3 -1
- package/src/dialog/Base.mjs +1 -2
- package/src/form/field/Time.mjs +18 -16
- package/src/layout/Base.mjs +43 -6
- package/src/layout/Card.mjs +21 -59
- package/src/layout/Cube.mjs +432 -0
- package/src/layout/Fit.mjs +9 -38
- package/src/layout/Flexbox.mjs +16 -17
- package/src/layout/Form.mjs +13 -70
- package/src/layout/Grid.mjs +6 -18
- package/src/list/Base.mjs +3 -3
- package/src/main/mixin/DeltaUpdates.mjs +16 -3
- package/src/model/Component.mjs +25 -6
- package/src/util/Array.mjs +36 -0
- package/src/vdom/Helper.mjs +338 -442
- package/src/vdom/VNode.mjs +12 -1
- package/test/siesta/siesta.js +16 -1
- package/test/siesta/tests/VdomCalendar.mjs +2193 -37
- package/test/siesta/tests/VdomHelper.mjs +287 -48
- package/test/siesta/tests/vdom/Advanced.mjs +368 -0
- package/test/siesta/tests/vdom/layout/Cube.mjs +189 -0
- package/test/siesta/tests/vdom/table/Container.mjs +133 -0
- package/resources/scss/theme-neo-light/apps/portal/learn/ContentTreeList.scss +0 -23
@@ -0,0 +1,217 @@
|
|
1
|
+
import ConfigurationViewport from '../../ConfigurationViewport.mjs';
|
2
|
+
import Container from '../../../src/container/Base.mjs';
|
3
|
+
import CubeLayout from '../../../src/layout/Cube.mjs';
|
4
|
+
import NumberField from '../../../src/form/field/Number.mjs';
|
5
|
+
import Radio from '../../../src/form/field/Radio.mjs';
|
6
|
+
import RangeField from '../../../src/form/field/Range.mjs';
|
7
|
+
import Toolbar from '../../../src/toolbar/Base.mjs';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* @class Neo.examples.layout.cube.MainContainer
|
11
|
+
* @extends Neo.examples.ConfigurationViewport
|
12
|
+
*/
|
13
|
+
class MainContainer extends ConfigurationViewport {
|
14
|
+
static config = {
|
15
|
+
className : 'Neo.examples.layout.cube.MainContainer',
|
16
|
+
cls : ['examples-layout-cube-maincontainer'],
|
17
|
+
configItemLabelWidth: 160,
|
18
|
+
configItemWidth : 280,
|
19
|
+
layout : {ntype: 'hbox', align: 'stretch'}
|
20
|
+
}
|
21
|
+
|
22
|
+
createConfigurationComponents() {
|
23
|
+
let me = this,
|
24
|
+
{layout} = me.exampleComponent.getItem('container');
|
25
|
+
|
26
|
+
return [{
|
27
|
+
module : Radio,
|
28
|
+
checked : layout.ntype === 'layout-cube',
|
29
|
+
hideValueLabel: false,
|
30
|
+
labelText : 'layout',
|
31
|
+
listeners : {change: me.onRadioLayoutChange.bind(me, 'cube')},
|
32
|
+
name : 'layout',
|
33
|
+
valueLabelText: 'Cube'
|
34
|
+
}, {
|
35
|
+
module : Radio,
|
36
|
+
checked : layout.ntype === 'layout-vbox',
|
37
|
+
hideValueLabel: false,
|
38
|
+
labelText : '',
|
39
|
+
listeners : {change: me.onRadioLayoutChange.bind(me, 'vbox')},
|
40
|
+
name : 'layout',
|
41
|
+
valueLabelText: 'VBox'
|
42
|
+
}, {
|
43
|
+
module : RangeField,
|
44
|
+
labelText: 'perspective',
|
45
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'perspective')},
|
46
|
+
maxValue : 2000,
|
47
|
+
minValue : 400,
|
48
|
+
stepSize : 10,
|
49
|
+
style : {marginTop: '20px'},
|
50
|
+
value : layout.perspective
|
51
|
+
}, {
|
52
|
+
module : RangeField,
|
53
|
+
labelText: 'rotateX',
|
54
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'rotateX')},
|
55
|
+
maxValue : 360,
|
56
|
+
minValue : 0,
|
57
|
+
stepSize : 1,
|
58
|
+
style : {marginTop: '20px'},
|
59
|
+
value : layout.rotateX
|
60
|
+
}, {
|
61
|
+
module : RangeField,
|
62
|
+
labelText: 'rotateY',
|
63
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'rotateY')},
|
64
|
+
maxValue : 360,
|
65
|
+
minValue : 0,
|
66
|
+
stepSize : 1,
|
67
|
+
value : layout.rotateY
|
68
|
+
}, {
|
69
|
+
module : RangeField,
|
70
|
+
labelText: 'rotateZ',
|
71
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'rotateZ')},
|
72
|
+
maxValue : 360,
|
73
|
+
minValue : 0,
|
74
|
+
stepSize : 1,
|
75
|
+
value : layout.rotateZ
|
76
|
+
}, {
|
77
|
+
module : RangeField,
|
78
|
+
labelText: 'sideX',
|
79
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'sideX')},
|
80
|
+
maxValue : 400,
|
81
|
+
minValue : 100,
|
82
|
+
stepSize : 10,
|
83
|
+
style : {marginTop: '20px'},
|
84
|
+
value : layout.sideX
|
85
|
+
}, {
|
86
|
+
module : RangeField,
|
87
|
+
labelText: 'sideY',
|
88
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'sideY')},
|
89
|
+
maxValue : 400,
|
90
|
+
minValue : 100,
|
91
|
+
stepSize : 10,
|
92
|
+
value : layout.sideY
|
93
|
+
}, {
|
94
|
+
module : RangeField,
|
95
|
+
labelText: 'sideZ',
|
96
|
+
listeners: {change: me.onLayoutConfigChange.bind(me, 'sideZ')},
|
97
|
+
maxValue : 400,
|
98
|
+
minValue : 100,
|
99
|
+
stepSize : 10,
|
100
|
+
value : layout.sideZ
|
101
|
+
}, {
|
102
|
+
module : NumberField,
|
103
|
+
clearable: true,
|
104
|
+
labelText: 'height',
|
105
|
+
listeners: {change: me.onConfigChange.bind(me, 'height')},
|
106
|
+
maxValue : 1000,
|
107
|
+
minValue : 300,
|
108
|
+
stepSize : 10,
|
109
|
+
style : {marginTop: '20px'},
|
110
|
+
value : me.exampleComponent.height
|
111
|
+
}, {
|
112
|
+
module : NumberField,
|
113
|
+
clearable: true,
|
114
|
+
labelText: 'width',
|
115
|
+
listeners: {change: me.onConfigChange.bind(me, 'width')},
|
116
|
+
maxValue : 1000,
|
117
|
+
minValue : 300,
|
118
|
+
stepSize : 10,
|
119
|
+
style : {marginTop: '10px'},
|
120
|
+
value : me.exampleComponent.width
|
121
|
+
}];
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* @returns {Neo.component.Base}
|
126
|
+
*/
|
127
|
+
createExampleComponent() {
|
128
|
+
return Neo.create({
|
129
|
+
module: Container,
|
130
|
+
height: 550,
|
131
|
+
layout: {ntype: 'vbox', align: 'center', pack: 'center'},
|
132
|
+
width : 550,
|
133
|
+
|
134
|
+
items: [{
|
135
|
+
module : Container,
|
136
|
+
reference: 'container',
|
137
|
+
style : {color: 'white', fontSize: '50px', margin: '50px', textAlign: 'center'},
|
138
|
+
|
139
|
+
layout: {
|
140
|
+
ntype : 'cube',
|
141
|
+
rotateX: 194,
|
142
|
+
rotateY: 213,
|
143
|
+
rotateZ: 162
|
144
|
+
},
|
145
|
+
|
146
|
+
items: [
|
147
|
+
{style: {backgroundColor: 'rgba(255, 0, 0, 0.5)'}, html: 'Front'},
|
148
|
+
{style: {backgroundColor: 'rgba( 0, 255, 0, 0.5)'}, html: 'Back'},
|
149
|
+
{style: {backgroundColor: 'rgba( 0, 0, 255, 0.5)'}, html: 'Left'},
|
150
|
+
{style: {backgroundColor: 'rgba( 0, 255, 255, 0.5)'}, html: 'Right'},
|
151
|
+
{style: {backgroundColor: 'rgba(255, 0, 255, 0.5)'}, html: 'Top'},
|
152
|
+
{style: {backgroundColor: 'rgba(255, 255, 0, 0.5)'}, html: 'Bottom'}
|
153
|
+
]
|
154
|
+
}, {
|
155
|
+
module: Toolbar,
|
156
|
+
flex : 'none',
|
157
|
+
style : {marginTop: '1em'},
|
158
|
+
|
159
|
+
itemDefaults: {
|
160
|
+
ntype : 'button',
|
161
|
+
handler: 'up.onFaceButtonClick',
|
162
|
+
style : {marginRight: '.3em'}
|
163
|
+
},
|
164
|
+
|
165
|
+
items: [
|
166
|
+
{text: 'Front'},
|
167
|
+
{text: 'Back'},
|
168
|
+
{text: 'Left'},
|
169
|
+
{text: 'Right'},
|
170
|
+
{text: 'Top'},
|
171
|
+
{text: 'Bottom'}
|
172
|
+
]
|
173
|
+
}]
|
174
|
+
})
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* @param {Object} data
|
179
|
+
*/
|
180
|
+
onFaceButtonClick(data) {
|
181
|
+
this.getItem('container').layout['activeFace'] = data.component.text.toLowerCase()
|
182
|
+
}
|
183
|
+
|
184
|
+
|
185
|
+
/**
|
186
|
+
* @param {String} config
|
187
|
+
* @param {Object} opts
|
188
|
+
*/
|
189
|
+
onLayoutConfigChange(config, opts) {
|
190
|
+
this.getItem('container').layout[config] = opts.value
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* @param {String} name
|
195
|
+
* @param {Object} opts
|
196
|
+
*/
|
197
|
+
onRadioLayoutChange(name, opts) {
|
198
|
+
if (opts.value === true) { // we only want to listen to check events, not uncheck
|
199
|
+
let layout = name;
|
200
|
+
|
201
|
+
if (name === 'cube') {
|
202
|
+
layout = {
|
203
|
+
ntype : 'cube',
|
204
|
+
rotateX: 194,
|
205
|
+
rotateY: 213,
|
206
|
+
rotateZ: 162
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
this.getItem('container').layout = layout;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
Neo.setupClass(MainContainer);
|
216
|
+
|
217
|
+
export default MainContainer;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE HTML>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>Neo Cube Layout</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<script src="../../../src/MicroLoader.mjs" type="module"></script>
|
10
|
+
</body>
|
11
|
+
</html>
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import ComponentModel from '../../../src/model/Component.mjs';
|
2
|
+
import Label from '../../../src/component/Label.mjs';
|
3
|
+
import TextField from '../../../src/form/field/Text.mjs';
|
4
|
+
import Viewport from '../../../src/container/Viewport.mjs';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @class Neo.examples.model.twoWay.MainContainer
|
8
|
+
* @extends Neo.container.Viewport
|
9
|
+
*/
|
10
|
+
class MainContainer extends Viewport {
|
11
|
+
static config = {
|
12
|
+
/**
|
13
|
+
* @member {String} className='Neo.examples.model.twoWay.MainContainer'
|
14
|
+
* @protected
|
15
|
+
*/
|
16
|
+
className: 'Neo.examples.model.twoWay.MainContainer',
|
17
|
+
/**
|
18
|
+
* @member {Object|Neo.model.Component} model
|
19
|
+
*/
|
20
|
+
model: {
|
21
|
+
data: {
|
22
|
+
user: {
|
23
|
+
details: {
|
24
|
+
firstname: 'Tobias',
|
25
|
+
lastname : 'Uhlig'
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
},
|
30
|
+
/**
|
31
|
+
* @member {Object[]} items
|
32
|
+
*/
|
33
|
+
items: [{
|
34
|
+
module : Label,
|
35
|
+
flex : 'none',
|
36
|
+
labelText : 'Firstname',
|
37
|
+
labelWidth: 110,
|
38
|
+
width : 300,
|
39
|
+
|
40
|
+
bind: {
|
41
|
+
text: data => data.user.details.firstname + ' ' + data.user.details.lastname
|
42
|
+
},
|
43
|
+
}, {
|
44
|
+
module : TextField,
|
45
|
+
flex : 'none',
|
46
|
+
labelText : 'Firstname',
|
47
|
+
labelWidth: 110,
|
48
|
+
style : {marginTop: '2em'},
|
49
|
+
width : 300,
|
50
|
+
|
51
|
+
bind: {
|
52
|
+
value: {twoWay: true, value: data => data.user.details.firstname}
|
53
|
+
},
|
54
|
+
}, {
|
55
|
+
module : TextField,
|
56
|
+
flex : 'none',
|
57
|
+
labelText : 'Lastname',
|
58
|
+
labelWidth: 110,
|
59
|
+
width : 300,
|
60
|
+
|
61
|
+
bind: {
|
62
|
+
value: {twoWay: true, value: data => data.user.details.lastname}
|
63
|
+
},
|
64
|
+
}],
|
65
|
+
/**
|
66
|
+
* @member {Object} style
|
67
|
+
*/
|
68
|
+
style: {
|
69
|
+
padding: '5em'
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
Neo.setupClass(MainContainer);
|
75
|
+
|
76
|
+
export default MainContainer;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE HTML>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>model.Component: 2 way binding</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<script src="../../../src/MicroLoader.mjs" type="module"></script>
|
10
|
+
</body>
|
11
|
+
</html>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.
|
3
|
+
"version": "6.19.1",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -43,25 +43,25 @@
|
|
43
43
|
},
|
44
44
|
"homepage": "https://neomjs.com/",
|
45
45
|
"devDependencies": {
|
46
|
-
"@fortawesome/fontawesome-free": "^6.
|
46
|
+
"@fortawesome/fontawesome-free": "^6.6.0",
|
47
47
|
"autoprefixer": "^10.4.19",
|
48
48
|
"chalk": "^5.3.0",
|
49
49
|
"clean-webpack-plugin": "^4.0.0",
|
50
50
|
"commander": "^12.1.0",
|
51
|
-
"cssnano": "^7.0.
|
51
|
+
"cssnano": "^7.0.4",
|
52
52
|
"envinfo": "^7.13.0",
|
53
53
|
"fs-extra": "^11.2.0",
|
54
54
|
"highlightjs-line-numbers.js": "^2.8.0",
|
55
|
-
"inquirer": "^
|
56
|
-
"marked": "^13.0.
|
55
|
+
"inquirer": "^10.0.4",
|
56
|
+
"marked": "^13.0.2",
|
57
57
|
"monaco-editor": "^0.50.0",
|
58
58
|
"neo-jsdoc": "1.0.1",
|
59
59
|
"neo-jsdoc-x": "1.0.5",
|
60
60
|
"postcss": "^8.4.39",
|
61
|
-
"sass": "^1.77.
|
61
|
+
"sass": "^1.77.8",
|
62
62
|
"siesta-lite": "5.5.2",
|
63
63
|
"url": "^0.11.3",
|
64
|
-
"webpack": "^5.
|
64
|
+
"webpack": "^5.93.0",
|
65
65
|
"webpack-cli": "^5.1.4",
|
66
66
|
"webpack-dev-server": "^5.0.4",
|
67
67
|
"webpack-hook-plugin": "^1.0.7",
|
@@ -0,0 +1,32 @@
|
|
1
|
+
In this topic you'll create an application that fetches data on earthquakes in Iceland,
|
2
|
+
and show the information in two views: a table, and a map.
|
3
|
+
You'll do this in a series of simple labs.
|
4
|
+
|
5
|
+
What are the goals of this lengthy topic?
|
6
|
+
|
7
|
+
- To give you hands-on coding a simple app
|
8
|
+
- To introduce fundamental Neo concepts
|
9
|
+
|
10
|
+
Most of these labs are copy-and-paste because we're focusing on _what_ the code is doing on rather than _how_.
|
11
|
+
|
12
|
+
For this tutorial don't worry about syntax details. Other tutorials and guides will spend more and
|
13
|
+
more time on syntax.e
|
14
|
+
|
15
|
+
## Key concepts
|
16
|
+
|
17
|
+
A few key concepts we'll be discussing:
|
18
|
+
|
19
|
+
- Creating a starter app
|
20
|
+
- Configuring components
|
21
|
+
- Debugging
|
22
|
+
- Class-based coding
|
23
|
+
- View models
|
24
|
+
- Events
|
25
|
+
- Controllers
|
26
|
+
|
27
|
+
## Advice
|
28
|
+
|
29
|
+
A word of advice: Keep a high-level perspective, especially early on. We'll have plenty of time to get
|
30
|
+
into the code, and we'll do most things multiple times. In other words, focus on what you're accomplishing,
|
31
|
+
and don't worry about syntax details.
|
32
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
In this lab, you'll generate a Neo.mjs workspace and run the starter app.
|
2
|
+
|
3
|
+
<details>
|
4
|
+
<summary>Wait!</summary>
|
5
|
+
You may already have a workspace! If so, you can skip this lab. For example, if you followed the <a href="#/learn/Setup">Getting Started > Setup</a> topic, above, you should already have a workspace.
|
6
|
+
|
7
|
+
If you don't have a workspace, then continue on to the next step.
|
8
|
+
</details>
|
9
|
+
|
10
|
+
<details>
|
11
|
+
<summary>Use the command-line to generate the workspace</summary>
|
12
|
+
|
13
|
+
Use a terminal window to navigate to some parent folder,
|
14
|
+
then run
|
15
|
+
|
16
|
+
npx neo-app@latest
|
17
|
+
|
18
|
+
You'll be prompted for a workspace name, starter app name, etc — accept the default for everything.
|
19
|
+
As the command finishes it starts a server and opens a browser window.
|
20
|
+
</details>
|
21
|
+
|
22
|
+
<details>
|
23
|
+
<summary>Inspect the workspace</summary>
|
24
|
+
|
25
|
+
The workspace contains a local copy of the API docs, an `apps` directory (where your apps are found),
|
26
|
+
and some other directories.
|
27
|
+
</details>
|
28
|
+
|
29
|
+
<details>
|
30
|
+
<summary>Start the server</summary>
|
31
|
+
From the root of the `workspace` start the server via `npm run server-start`. That starts a server
|
32
|
+
at port 8080 and opens a new browser window.
|
33
|
+
|
34
|
+
<img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/StartServer.png" style="width:80%"/>
|
35
|
+
|
36
|
+
</details>
|
37
|
+
|
38
|
+
|
39
|
+
<details>
|
40
|
+
<summary>Run the starter app</summary>
|
41
|
+
|
42
|
+
By default, an app named `myapp` was created. You can run it by entering the `apps` directory and
|
43
|
+
clicking `myapp`. It's a folder containing an `index.html` along with the source code for the app.
|
44
|
+
|
45
|
+
<img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/RunTheStarterApp.png" style="width:80%"/>
|
46
|
+
|
47
|
+
</details>
|
@@ -0,0 +1,150 @@
|
|
1
|
+
In this lab you'll create a starter app and add a single component.
|
2
|
+
|
3
|
+
<details>
|
4
|
+
|
5
|
+
<summary>Use the command-line to create a starter app</summary>
|
6
|
+
|
7
|
+
Use a terminal window to navigate to the workspace and run the following script. Use "Earthquakes"
|
8
|
+
as the app name, and defaults for everything else.
|
9
|
+
|
10
|
+
npm run create-app-minimal
|
11
|
+
|
12
|
+
After the script runs yous should see these files in the `app/earthquakes` directory.
|
13
|
+
|
14
|
+
`view/MainContainer.mjs`
|
15
|
+
- `app.mjs`
|
16
|
+
- `index.html`
|
17
|
+
- `neo-config.json`
|
18
|
+
|
19
|
+
If you look in `neo-config.json` you should see this content. Note the `mainThreadAddons` block
|
20
|
+
— it reflects the add-ons you chose when you followed the instructions in the script.
|
21
|
+
<pre>
|
22
|
+
{
|
23
|
+
"appPath": "../../apps/earthquakes/app.mjs",
|
24
|
+
"basePath": "../../",
|
25
|
+
"environment": "development",
|
26
|
+
"mainPath": "../node_modules/neo.mjs/src/Main.mjs",
|
27
|
+
"mainThreadAddons": [
|
28
|
+
"DragDrop",
|
29
|
+
"Stylesheet"
|
30
|
+
],
|
31
|
+
"workerBasePath": "../../node_modules/neo.mjs/src/worker/"
|
32
|
+
}</pre>
|
33
|
+
|
34
|
+
You're free to edit `neo-config.json` if you were to change your mind later about the theme or need for other add-ons.
|
35
|
+
|
36
|
+
If you refresh browser at <a href="http://localhost:8080/apps/" target="apps">http://localhost:8080/apps/</a>
|
37
|
+
you'll see the new _earthquakes_ app listed, and if you run it you'll see... nothing! That's because the
|
38
|
+
minimal starter app is the shell of an application without any view content. We'll add a little content
|
39
|
+
later in the lab.
|
40
|
+
|
41
|
+
<img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EmptyEarthquakes.png"></img>
|
42
|
+
|
43
|
+
</details>
|
44
|
+
|
45
|
+
<details>
|
46
|
+
<summary>Look at the main view source</summary>
|
47
|
+
|
48
|
+
Use a code editor and look at `workspace/apps/earthquakes/src/view/MainView.mjs`. You'll see the
|
49
|
+
following class definition:
|
50
|
+
|
51
|
+
<pre data-javascript>
|
52
|
+
|
53
|
+
import Base from '../../../node_modules/neo.mjs/src/container/Base.mjs';
|
54
|
+
import Controller from './MainViewController.mjs';
|
55
|
+
import ViewModel from './MainViewModel.mjs';
|
56
|
+
|
57
|
+
class MainView extends Base {
|
58
|
+
static config = {
|
59
|
+
className: 'Earthquakes.view.MainView',
|
60
|
+
ntype: 'earthquakes-main',
|
61
|
+
|
62
|
+
controller: {module: Controller},
|
63
|
+
model: {module: ViewModel},
|
64
|
+
|
65
|
+
layout: {ntype: 'fit'},
|
66
|
+
items: [],
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
Neo.setupClass(MainView);
|
71
|
+
|
72
|
+
export default MainView;
|
73
|
+
</pre>
|
74
|
+
|
75
|
+
As you can see, `MainView extends Base`, and `Base` is a _container_ (`Neo.container.Base`).
|
76
|
+
A container is a component — it holds other components, specified in `items:[]`. There
|
77
|
+
are no items in the starter app. The `layout` config specifies how the items are arranged.
|
78
|
+
|
79
|
+
</details>
|
80
|
+
|
81
|
+
<details>
|
82
|
+
<summary>Add a component</summary>
|
83
|
+
|
84
|
+
Let's add a button. To do that, add an import for the button base class, then configure it
|
85
|
+
in the container's `items:[]`. If you were to read the API docs for buttons, you'd see
|
86
|
+
that buttons have various configs, such as `text`, which is the button text, `iconCls`, which
|
87
|
+
is typically a FontAwesome CSS class used to show an icon, and `handler`, which specifies
|
88
|
+
which method to run when the button is clicked. We'll use `text`.
|
89
|
+
|
90
|
+
<pre data-javascript>
|
91
|
+
|
92
|
+
import Base from '../../../node_modules/neo.mjs/src/container/Base.mjs';
|
93
|
+
import Button from '../../../node_modules/neo.mjs/src/button/Base.mjs';
|
94
|
+
import Controller from './MainViewController.mjs';
|
95
|
+
import ViewModel from './MainViewModel.mjs';
|
96
|
+
|
97
|
+
class MainView extends Base {
|
98
|
+
static config = {
|
99
|
+
className: 'Earthquakes.view.MainView',
|
100
|
+
ntype: 'earthquakes-main',
|
101
|
+
|
102
|
+
controller: {module: Controller},
|
103
|
+
model: {module: ViewModel},
|
104
|
+
|
105
|
+
layout: {ntype: 'fit'},
|
106
|
+
items: [{
|
107
|
+
module: Button,
|
108
|
+
text: 'Button!'
|
109
|
+
}],
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
Neo.setupClass(MainView);
|
114
|
+
|
115
|
+
export default MainView;
|
116
|
+
</pre>
|
117
|
+
|
118
|
+
|
119
|
+
When you run the app you'll see the single button.
|
120
|
+
|
121
|
+
<img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EarthquakesSingleFitButton.png" style="width:80%"/>
|
122
|
+
|
123
|
+
The button takes up the full width. Buttons look different depending on the theme. We're using
|
124
|
+
the _neo-theme-neo-light_ theme, which controls button height. Otherwise, child items a using the _fit_ layout
|
125
|
+
take up the full window.
|
126
|
+
</details>
|
127
|
+
|
128
|
+
|
129
|
+
<details>
|
130
|
+
<summary>Modify the layout</summary>
|
131
|
+
The `layout` configures how child items are visually arranged. First, note that the config
|
132
|
+
specifies `ntype`. We used `module` for the button config. An `ntype` is a class alias — if a class
|
133
|
+
has already been imported, you can use the `ntype` rather than importing it again and using the `module`
|
134
|
+
config. We haven't imported any layouts, but it turns out that `Neo.container.Base` _does_ import all the
|
135
|
+
layout types, which means we're always free to use `ntype` with layouts. You're free to specify an `ntype`
|
136
|
+
for the classes you define.
|
137
|
+
|
138
|
+
Let's change the layout to arrange items vertically, with items aligned horizontally at the start.
|
139
|
+
|
140
|
+
<pre data-javascript>
|
141
|
+
layout: {
|
142
|
+
ntype: 'vbox',
|
143
|
+
align: 'start'
|
144
|
+
}
|
145
|
+
</pre>
|
146
|
+
|
147
|
+
<img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EarthquakesSingleVoxStartButton.png" style="width:80%"/>
|
148
|
+
|
149
|
+
|
150
|
+
</details>
|