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.
Files changed (94) hide show
  1. package/README.md +28 -214
  2. package/apps/ServiceWorker.mjs +2 -2
  3. package/apps/colors/view/ViewportController.mjs +7 -3
  4. package/apps/portal/data/blog.json +13 -0
  5. package/apps/portal/view/HeaderToolbar.mjs +2 -2
  6. package/apps/portal/view/Viewport.mjs +4 -2
  7. package/apps/portal/view/ViewportController.mjs +88 -7
  8. package/apps/portal/view/blog/Container.mjs +8 -8
  9. package/apps/portal/view/blog/List.mjs +6 -6
  10. package/apps/portal/view/home/FooterContainer.mjs +123 -0
  11. package/apps/portal/view/home/MainContainer.mjs +3 -2
  12. package/apps/portal/view/home/parts/AfterMath.mjs +17 -24
  13. package/apps/portal/view/home/parts/Colors.mjs +2 -2
  14. package/apps/portal/view/home/parts/How.mjs +3 -3
  15. package/apps/portal/view/home/parts/MainNeo.mjs +6 -7
  16. package/apps/portal/view/home/parts/References.mjs +88 -0
  17. package/apps/portal/view/learn/MainContainer.mjs +3 -2
  18. package/apps/portal/view/learn/MainContainerController.mjs +11 -0
  19. package/apps/portal/view/learn/PageContainer.mjs +5 -3
  20. package/apps/portal/view/services/Component.mjs +73 -0
  21. package/apps/website/data/blog.json +13 -0
  22. package/examples/ServiceWorker.mjs +2 -2
  23. package/examples/component/carousel/MainContainer.mjs +42 -33
  24. package/examples/layout/cube/MainContainer.mjs +217 -0
  25. package/examples/layout/cube/app.mjs +6 -0
  26. package/examples/layout/cube/index.html +11 -0
  27. package/examples/layout/cube/neo-config.json +6 -0
  28. package/examples/model/twoWay/MainContainer.mjs +76 -0
  29. package/examples/model/twoWay/app.mjs +6 -0
  30. package/examples/model/twoWay/index.html +11 -0
  31. package/examples/model/twoWay/neo-config.json +6 -0
  32. package/package.json +7 -7
  33. package/resources/data/deck/learnneo/pages/Earthquakes-01-goals.md +32 -0
  34. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-01-generate-a-workspace.md +47 -0
  35. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-02-generate-the-starter-app.md +150 -0
  36. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-03-debugging.md +136 -0
  37. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-04-fetch-data.md +146 -0
  38. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-05-refactor-the-table.md +146 -0
  39. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-06-use-a-view-model.md +301 -0
  40. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-07-use-the-google-maps-addon.md +175 -0
  41. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-08-events.md +38 -0
  42. package/resources/data/deck/learnneo/pages/Earthquakes.md +8 -8
  43. package/resources/data/deck/learnneo/pages/Glossary.md +0 -0
  44. package/resources/data/deck/learnneo/pages/GuideEvents.md +80 -1
  45. package/resources/data/deck/learnneo/tree.json +2 -1
  46. package/resources/images/apps/portal/neo-references.png +0 -0
  47. package/resources/scss/src/apps/portal/Viewport.scss +18 -0
  48. package/resources/scss/src/apps/portal/blog/Container.scss +7 -7
  49. package/resources/scss/src/apps/portal/blog/List.scss +20 -16
  50. package/resources/scss/src/apps/portal/home/FooterContainer.scss +31 -0
  51. package/resources/scss/src/apps/portal/home/parts/AfterMath.scss +5 -0
  52. package/resources/scss/src/apps/portal/home/parts/MainNeo.scss +4 -5
  53. package/resources/scss/src/apps/portal/home/parts/References.scss +46 -0
  54. package/resources/scss/src/apps/portal/learn/ContentTreeList.scss +20 -0
  55. package/resources/scss/src/apps/portal/learn/ContentView.scss +4 -0
  56. package/resources/scss/src/apps/portal/learn/MainContainer.scss +1 -1
  57. package/resources/scss/src/apps/portal/learn/PageContainer.scss +22 -16
  58. package/resources/scss/src/apps/portal/services/Component.scss +20 -0
  59. package/resources/scss/src/component/Carousel.scss +21 -0
  60. package/resources/scss/src/examples/layout/cube/MainContainer.scss +7 -0
  61. package/resources/scss/src/layout/Cube.scss +80 -0
  62. package/resources/scss/src/tab/Container.scss +10 -10
  63. package/resources/scss/theme-neo-light/apps/portal/blog/Container.scss +3 -0
  64. package/resources/scss/theme-neo-light/form/field/Search.scss +1 -1
  65. package/resources/scss/theme-neo-light/tooltip/Base.scss +1 -1
  66. package/src/DefaultConfig.mjs +2 -2
  67. package/src/Main.mjs +15 -1
  68. package/src/Neo.mjs +14 -3
  69. package/src/calendar/view/MainContainer.mjs +8 -7
  70. package/src/component/Base.mjs +28 -8
  71. package/src/component/DateSelector.mjs +2 -2
  72. package/src/container/Base.mjs +3 -1
  73. package/src/dialog/Base.mjs +1 -2
  74. package/src/form/field/Time.mjs +18 -16
  75. package/src/layout/Base.mjs +43 -6
  76. package/src/layout/Card.mjs +21 -59
  77. package/src/layout/Cube.mjs +432 -0
  78. package/src/layout/Fit.mjs +9 -38
  79. package/src/layout/Flexbox.mjs +16 -17
  80. package/src/layout/Form.mjs +13 -70
  81. package/src/layout/Grid.mjs +6 -18
  82. package/src/list/Base.mjs +3 -3
  83. package/src/main/mixin/DeltaUpdates.mjs +16 -3
  84. package/src/model/Component.mjs +25 -6
  85. package/src/util/Array.mjs +36 -0
  86. package/src/vdom/Helper.mjs +338 -442
  87. package/src/vdom/VNode.mjs +12 -1
  88. package/test/siesta/siesta.js +16 -1
  89. package/test/siesta/tests/VdomCalendar.mjs +2193 -37
  90. package/test/siesta/tests/VdomHelper.mjs +287 -48
  91. package/test/siesta/tests/vdom/Advanced.mjs +368 -0
  92. package/test/siesta/tests/vdom/layout/Cube.mjs +189 -0
  93. package/test/siesta/tests/vdom/table/Container.mjs +133 -0
  94. 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,6 @@
1
+ import MainContainer from './MainContainer.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: MainContainer,
5
+ name : 'Neo.examples.layout.cube'
6
+ });
@@ -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,6 @@
1
+ {
2
+ "appPath" : "examples/layout/cube/app.mjs",
3
+ "basePath" : "../../../",
4
+ "environment": "development",
5
+ "mainPath" : "./Main.mjs"
6
+ }
@@ -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,6 @@
1
+ import MainContainer from './MainContainer.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: MainContainer,
5
+ name : 'Neo.examples.model.twoWay'
6
+ });
@@ -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>
@@ -0,0 +1,6 @@
1
+ {
2
+ "appPath" : "examples/model/twoWay/app.mjs",
3
+ "basePath" : "../../../",
4
+ "environment": "development",
5
+ "mainPath" : "./Main.mjs"
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.18.3",
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.5.2",
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.3",
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": "^9.3.2",
56
- "marked": "^13.0.1",
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.6",
61
+ "sass": "^1.77.8",
62
62
  "siesta-lite": "5.5.2",
63
63
  "url": "^0.11.3",
64
- "webpack": "^5.92.1",
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 &mdash; 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
+ &mdash; 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 &mdash; 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 &mdash; 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>