neo.mjs 6.18.2 → 6.19.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.
Files changed (108) 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 +89 -8
  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/MainContainer.mjs +9 -10
  11. package/apps/portal/view/home/parts/BaseContainer.mjs +8 -1
  12. package/apps/portal/view/home/parts/Colors.mjs +4 -4
  13. package/apps/portal/view/home/parts/Helix.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/ContentView.mjs +3 -1
  18. package/apps/portal/view/learn/MainContainer.mjs +3 -2
  19. package/apps/portal/view/learn/MainContainerController.mjs +11 -0
  20. package/apps/portal/view/learn/PageContainer.mjs +5 -3
  21. package/apps/portal/view/services/Component.mjs +73 -0
  22. package/apps/website/data/blog.json +13 -0
  23. package/examples/ServiceWorker.mjs +2 -2
  24. package/examples/component/carousel/MainContainer.mjs +42 -33
  25. package/examples/layout/cube/MainContainer.mjs +217 -0
  26. package/examples/layout/cube/app.mjs +6 -0
  27. package/examples/layout/cube/index.html +11 -0
  28. package/examples/layout/cube/neo-config.json +6 -0
  29. package/package.json +7 -7
  30. package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +2 -2
  31. package/resources/data/deck/learnneo/pages/ComponentModels.md +6 -6
  32. package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +10 -10
  33. package/resources/data/deck/learnneo/pages/Config.md +6 -6
  34. package/resources/data/deck/learnneo/pages/CustomComponents.md +4 -4
  35. package/resources/data/deck/learnneo/pages/DescribingTheUI.md +4 -4
  36. package/resources/data/deck/learnneo/pages/Earthquakes-01-goals.md +32 -0
  37. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-01-generate-a-workspace.md +47 -0
  38. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-02-generate-the-starter-app.md +150 -0
  39. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-03-debugging.md +136 -0
  40. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-04-fetch-data.md +146 -0
  41. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-05-refactor-the-table.md +146 -0
  42. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-06-use-a-view-model.md +301 -0
  43. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-07-use-the-google-maps-addon.md +175 -0
  44. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-08-events.md +38 -0
  45. package/resources/data/deck/learnneo/pages/Earthquakes.md +10 -10
  46. package/resources/data/deck/learnneo/pages/Events.md +7 -7
  47. package/resources/data/deck/learnneo/pages/Extending.md +7 -7
  48. package/resources/data/deck/learnneo/pages/Glossary.md +0 -0
  49. package/resources/data/deck/learnneo/pages/GuideEvents.md +97 -19
  50. package/resources/data/deck/learnneo/pages/GuideViewModels.md +21 -21
  51. package/resources/data/deck/learnneo/pages/References.md +8 -8
  52. package/resources/data/deck/learnneo/pages/TestLivePreview.md +5 -4
  53. package/resources/data/deck/learnneo/pages/TodoList.md +9 -9
  54. package/resources/data/deck/learnneo/pages/Welcome.md +3 -3
  55. package/resources/data/deck/learnneo/pages/WhyNeo-Multi-Window.md +2 -2
  56. package/resources/data/deck/learnneo/pages/WhyNeo-Speed.md +2 -2
  57. package/resources/data/deck/learnneo/tree.json +2 -1
  58. package/resources/images/apps/portal/neo-references.png +0 -0
  59. package/resources/scss/src/apps/portal/HeaderToolbar.scss +0 -46
  60. package/resources/scss/src/apps/portal/Viewport.scss +16 -1
  61. package/resources/scss/src/apps/portal/blog/Container.scss +7 -7
  62. package/resources/scss/src/apps/portal/blog/List.scss +20 -16
  63. package/resources/scss/src/apps/portal/home/parts/BaseContainer.scss +33 -4
  64. package/resources/scss/src/apps/portal/home/parts/MainNeo.scss +4 -5
  65. package/resources/scss/src/apps/portal/home/parts/References.scss +46 -0
  66. package/resources/scss/src/apps/portal/learn/ContentTreeList.scss +20 -0
  67. package/resources/scss/src/apps/portal/learn/ContentView.scss +4 -0
  68. package/resources/scss/src/apps/portal/learn/MainContainer.scss +1 -1
  69. package/resources/scss/src/apps/portal/learn/PageContainer.scss +22 -16
  70. package/resources/scss/src/apps/portal/services/Component.scss +20 -0
  71. package/resources/scss/src/{apps/portal/learn → code}/LivePreview.scss +1 -1
  72. package/resources/scss/src/component/Carousel.scss +21 -0
  73. package/resources/scss/src/component/Helix.scss +1 -2
  74. package/resources/scss/src/examples/layout/cube/MainContainer.scss +7 -0
  75. package/resources/scss/src/layout/Cube.scss +80 -0
  76. package/resources/scss/src/tab/Container.scss +10 -10
  77. package/resources/scss/theme-neo-light/apps/portal/blog/Container.scss +3 -0
  78. package/resources/scss/theme-neo-light/form/field/Search.scss +1 -1
  79. package/resources/scss/theme-neo-light/tooltip/Base.scss +1 -1
  80. package/src/DefaultConfig.mjs +2 -2
  81. package/src/Main.mjs +15 -1
  82. package/src/Neo.mjs +14 -3
  83. package/{apps/portal/view/learn → src/code}/LivePreview.mjs +43 -27
  84. package/src/component/Base.mjs +18 -1
  85. package/src/container/Base.mjs +3 -1
  86. package/src/dialog/Base.mjs +1 -2
  87. package/src/layout/Base.mjs +43 -6
  88. package/src/layout/Card.mjs +21 -59
  89. package/src/layout/Cube.mjs +428 -0
  90. package/src/layout/Fit.mjs +9 -38
  91. package/src/layout/Flexbox.mjs +16 -17
  92. package/src/layout/Form.mjs +13 -70
  93. package/src/layout/Grid.mjs +6 -18
  94. package/src/main/addon/ResizeObserver.mjs +18 -2
  95. package/src/main/mixin/DeltaUpdates.mjs +16 -3
  96. package/src/util/Array.mjs +36 -0
  97. package/src/vdom/Helper.mjs +328 -445
  98. package/src/vdom/VNode.mjs +12 -1
  99. package/test/siesta/siesta.js +16 -1
  100. package/test/siesta/tests/VdomCalendar.mjs +2111 -37
  101. package/test/siesta/tests/VdomHelper.mjs +283 -47
  102. package/test/siesta/tests/vdom/Advanced.mjs +367 -0
  103. package/test/siesta/tests/vdom/layout/Cube.mjs +189 -0
  104. package/test/siesta/tests/vdom/table/Container.mjs +133 -0
  105. package/apps/portal/view/home/parts/HelloWorld.mjs +0 -83
  106. package/apps/portal/view/home/preview/PageCodeContainer.mjs +0 -55
  107. package/resources/scss/src/apps/portal/home/preview/PageCodeContainer.scss +0 -115
  108. package/resources/scss/theme-neo-light/apps/portal/learn/ContentTreeList.scss +0 -23
@@ -0,0 +1,301 @@
1
+ <details>
2
+ <summary>Look at network traffic</summary>
3
+
4
+ Before making any changes, open devtools in the Network tab and refresh _earthquakes_. You'll see two
5
+ calls to the web service.
6
+
7
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EarthquakesTwoTablesTwoCalls.png"></img>
8
+
9
+ </details>
10
+
11
+ <details>
12
+ <summary>Copy the store config to the view model</summary>
13
+
14
+ View models have two key configs: `data` and `stores`.
15
+
16
+ - `data` holds name/value pairs where the value can be a simple value, or object references
17
+ - `stores` holds configs of stores
18
+
19
+ Add a `stores` property to the view model config that holds a copy of the store.
20
+
21
+ <pre data-javascript>
22
+ import Base from '../../../node_modules/neo.mjs/src/container/Base.mjs';
23
+ import Controller from './MainViewController.mjs';
24
+ import EarthquakesTable from './earthquakes/Table.mjs';
25
+ import Store from '../../../node_modules/neo.mjs/src/data/Store.mjs';
26
+ import ViewModel from './MainViewModel.mjs';
27
+
28
+ class MainView extends Base {
29
+ static config = {
30
+ className: 'Earthquakes.view.MainView',
31
+ ntype: 'earthquakes-main',
32
+
33
+ controller: {module: Controller},
34
+ model: {
35
+ module: ViewModel,
36
+ stores: {
37
+ earthquakes: {
38
+ module: Store,
39
+ model: {
40
+ fields: [{
41
+ name: "humanReadableLocation"
42
+ }, {
43
+ name: "size"
44
+ }, {
45
+ name: "timestamp",
46
+ type: "Date"
47
+ }]
48
+ },
49
+ url: "https://apis.is/earthquake/is",
50
+ responseRoot: "results",
51
+ autoLoad: true
52
+ },
53
+ }
54
+ },
55
+
56
+ layout: {
57
+ ntype: 'vbox', align: 'stretch'
58
+ },
59
+ items: [{
60
+ module: EarthquakesTable,
61
+ store: {
62
+ module: Store,
63
+ model: {
64
+ fields: [{
65
+ name: "humanReadableLocation"
66
+ }, {
67
+ name: "size"
68
+ }, {
69
+ name: "timestamp",
70
+ type: "Date"
71
+ }]
72
+ },
73
+ url: "https://apis.is/earthquake/is",
74
+ responseRoot: "results",
75
+ autoLoad: true
76
+ },
77
+ style: {width: '100%'}
78
+ },{
79
+ module: EarthquakesTable,
80
+ store: {
81
+ module: Store,
82
+ model: {
83
+ fields: [{
84
+ name: "humanReadableLocation"
85
+ }, {
86
+ name: "size"
87
+ }, {
88
+ name: "timestamp",
89
+ type: "Date"
90
+ }]
91
+ },
92
+ url: "https://apis.is/earthquake/is",
93
+ responseRoot: "results",
94
+ autoLoad: true
95
+ },
96
+ style: {width: '100%'}
97
+ }],
98
+ }
99
+ }
100
+
101
+ Neo.setupClass(MainView);
102
+
103
+ export default MainView;
104
+
105
+ </pre>
106
+
107
+ In the `stores` config we named the store _earthquakes_. We could have named it anything, like _foo_
108
+ or _myStore_. We're calling it _earthquakes_ simply because that seems like a good descriptive name
109
+ of the data the store holds.
110
+
111
+ At this point we have _three_ identical store configs! Save and refresh, and look at network traffic &mdash; you
112
+ should see three calls.
113
+
114
+ Having an instance in the view model means we can share it. It can be shared anywhere in the containment
115
+ hierarchy. The app doesn't have much of a hierarchy: it's just the main view and two child components (the two
116
+ tables). But now that the store is in the parent's view model we can share it.
117
+
118
+ </details>
119
+
120
+ <details>
121
+ <summary>Use the shared store</summary>
122
+
123
+ The way to bind an instance to a view model property is with the `bind` config. For example
124
+
125
+ bind: {
126
+ store: 'stores.earthquakes'
127
+ }
128
+
129
+ binds a `store` property to a store called `foo`. The code is saying _in the future, when the value
130
+ of "stores.earthquakes" changes, assign it to this object's "store" property_. In this case, `stores.earthquakes`
131
+ starts out undefined, then at runtime within a few milliseconds as the view model is processed, the configured
132
+ store is created and a reference is assigned to `stores.earthquakes`. That wakes the binding up, and the
133
+ value is assigned to the table's `store` property.
134
+
135
+ Replace each table's `store` config with the binding.
136
+
137
+ <pre data-javascript>
138
+
139
+ import Base from '../../../node_modules/neo.mjs/src/container/Base.mjs';
140
+ import Controller from './MainViewController.mjs';
141
+ import EarthquakesTable from './earthquakes/Table.mjs';
142
+ import Store from '../../../node_modules/neo.mjs/src/data/Store.mjs';
143
+ import ViewModel from './MainViewModel.mjs';
144
+
145
+ class MainView extends Base {
146
+ static config = {
147
+ className: 'Earthquakes.view.MainView',
148
+ ntype: 'earthquakes-main',
149
+ controller: {module: Controller},
150
+ model: {
151
+ module: ViewModel,
152
+ stores: {
153
+ earthquakes: {
154
+ module: Store,
155
+ model: {
156
+ fields: [{
157
+ name: "humanReadableLocation"
158
+ }, {
159
+ name: "size"
160
+ }, {
161
+ name: "timestamp",
162
+ type: "Date"
163
+ }]
164
+ },
165
+ url: "https://apis.is/earthquake/is",
166
+ responseRoot: "results",
167
+ autoLoad: true
168
+ },
169
+ }
170
+ },
171
+
172
+ layout: { ntype: 'vbox', align: 'stretch' },
173
+ items: [{
174
+ module: EarthquakesTable,
175
+ bind: {
176
+ store: 'stores.earthquakes'
177
+ },
178
+ style: {width: '100%'}
179
+ },{
180
+ module: EarthquakesTable,
181
+ bind: {
182
+ store: 'stores.earthquakes'
183
+ },
184
+ style: {width: '100%'}
185
+ }],
186
+ }
187
+ }
188
+
189
+ Neo.setupClass(MainView);
190
+
191
+ export default MainView;
192
+ </pre>
193
+
194
+ Save, refresh, and look at network traffic: you'll see a _single_ call to the web service.
195
+
196
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EarthquakesTwoTablesOneCall.png"></img>
197
+
198
+ You can further prove we're using a shared instance by running these statements in the console.
199
+
200
+ <pre data-javascript>
201
+ a = Neo.findFirst({ntype:'earthquakes-main'}).model.stores.earthquakes;
202
+ b = Neo.find({ntype:'earthquakes-table'})[0].store;
203
+ c = Neo.find({ntype:'earthquakes-table'})[1].store;
204
+
205
+ (a === b) && (a === c) && (b === c) // true
206
+ </pre>
207
+
208
+ </details>
209
+
210
+ <details>
211
+ <summary>Use the view model class</summary>
212
+
213
+ We configured the view model in-line, in the `model` config at the top of `MainView`. But the starter app
214
+ has a `MainViewModel` class. In theory, if you have a trivial view model you could configure it in-line. But
215
+ in general you want to keep that code separate by coding it in a separate class. This is what we did for the
216
+ table config &mdash; we started by coding it in-line in the main view, then we refactored it into its own
217
+ class. The result was a simpler and more abstract main view. We want to do the same for the view model.
218
+
219
+ Since the starter app already provides `MainViewModel`, all we need to do is copy the `stores` property.
220
+
221
+ Here's the resulting code you should place into `MainViewModel.mjs`.
222
+
223
+ <pre data-javascript>
224
+ import Model from '../../../node_modules/neo.mjs/src/model/Component.mjs';
225
+ import Store from '../../../node_modules/neo.mjs/src/data/Store.mjs';
226
+
227
+ class MainViewModel extends Model {
228
+ static config = {
229
+ className: 'Earthquakes.view.MainViewModel',
230
+
231
+ data: {},
232
+ stores: {
233
+ earthquakes: {
234
+ module: Store,
235
+ model: {
236
+ fields: [{
237
+ name: "humanReadableLocation"
238
+ }, {
239
+ name: "size"
240
+ }, {
241
+ name: "timestamp",
242
+ type: "Date"
243
+ }]
244
+ },
245
+ url: "https://apis.is/earthquake/is",
246
+ responseRoot: "results",
247
+ autoLoad: true
248
+ },
249
+ }
250
+ }
251
+ }
252
+
253
+ Neo.setupClass(MainViewModel);
254
+
255
+ export default MainViewModel;
256
+ </pre>
257
+
258
+ And you need to remove the `stores` config from the main view as follows.
259
+
260
+ <pre data-javascript>
261
+ import Container from '../../../node_modules/neo.mjs/src/container/Base.mjs';
262
+ import Controller from './MainViewController.mjs';
263
+ import EarthquakesTable from './earthquakes/Table.mjs';
264
+ import ViewModel from './MainViewModel.mjs';
265
+
266
+ class MainView extends Container {
267
+ static config = {
268
+ className: 'Earthquakes.view.MainView',
269
+ ntype: 'earthquakes-main',
270
+ controller: {module: Controller},
271
+ model: {
272
+ module: ViewModel
273
+ },
274
+
275
+ layout: { ntype: 'vbox', align: 'stretch' },
276
+ items: [{
277
+ module: EarthquakesTable,
278
+ bind: {
279
+ store: 'stores.earthquakes'
280
+ },
281
+ style: {width: '100%'}
282
+ },{
283
+ module: EarthquakesTable,
284
+ bind: {
285
+ store: 'stores.earthquakes'
286
+ },
287
+ style: {width: '100%'}
288
+ }]
289
+ }
290
+ }
291
+
292
+ Neo.setupClass(MainView);
293
+
294
+ export default MainView;
295
+ </pre>
296
+
297
+ The refactorings to have separate table and view model classes means the code is more modular, more reusable,
298
+ and each class is simpler than using complex source files that try to configure every detail.
299
+
300
+ </details>
301
+
@@ -0,0 +1,175 @@
1
+ <details>
2
+ <summary>Get the code for the custom add-on</summary>
3
+ At the time this tutorial was written, the Neo.mjs Google Maps addon was about to be updated to
4
+ accomodate Google's "AdvancedMarker" class. Until that's ready, we're going to use a modified version of the add-on.
5
+
6
+ Download and unzip this file, and copy the two source files to the corresponding subdirectories in
7
+ your workspace's `src` directory. Note that `src` already contains some files, so don't replace the whole
8
+ directory, but instead, move the files to their individual locations.
9
+
10
+ <a href="https://s3.amazonaws.com/mjs.neo.learning.images/zip/src.zip">src.zip</a>
11
+
12
+ <img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/CopyGoogleMapsFiles.png" width="30%%"></img>
13
+ </details>
14
+
15
+ <details>
16
+ <summary>Specify the main-thread add-on</summary>
17
+
18
+ Edit `apps/earthquakes/neo-config.json` and add entries for the Google Maps add-on and the map key.
19
+
20
+ <pre data-javascript>
21
+ {
22
+ "appPath": "../../apps/earthquakes/app.mjs",
23
+ "basePath": "../../",
24
+ "environment": "development",
25
+ "mainPath": "../node_modules/neo.mjs/src/Main.mjs",
26
+ "mainThreadAddons": [
27
+ "DragDrop",
28
+ "WS/GoogleMaps",
29
+ "Stylesheet"
30
+ ],
31
+ "googleMapsApiKey": "AIzaSyD4Y2xvl9mGT8HiVvQiZluT5gah3OIveCE",
32
+ "themes" : ["neo-theme-neo-light"],
33
+ "workerBasePath": "../../node_modules/neo.mjs/src/worker/"
34
+ }
35
+ </pre>
36
+
37
+ It's unusual to need to edit `neo-config.json`. The app theme is specified there, and so are main thread add-ons.
38
+ In our case, we're adding `WS/GoogleMaps` which in turn requires that we specify the map key. The `WS/`
39
+ prefix tells Neo.mjs that the add-on is in our workspace, rather than an add-on provided by Neo.mjs.
40
+
41
+ Save and refresh, and you'll see a console log emanating from the plugin.
42
+
43
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/GoogleMapsLoaded.png"></img>
44
+
45
+ </details>
46
+
47
+ <details>
48
+ <summary>Add the required fields to the records</summary>
49
+
50
+ The Google Maps component has a `markerStore` property, which is a reference to a store whose records have
51
+ the properties `title` and `location`, where `location` is of the form `{lat: 0, lng: 0}`. The `fields:[]`
52
+ lets us implement those via two properties:
53
+
54
+ - `mapping` &mdash; the path to a feed property holding the value
55
+ - `calculate` &mdash; a function that returns a value
56
+
57
+ Edit `apps/earthquakes/view/MainViewModel.mjs` and modify `fields` as follows.
58
+
59
+ <pre data-javascript>
60
+ fields: [{
61
+ name: "humanReadableLocation",
62
+ }, {
63
+ name: "size",
64
+ }, {
65
+ name: "timestamp",
66
+ type: "Date",
67
+ }, {
68
+ name: 'title',
69
+ mapping: "humanReadableLocation"
70
+ }, {
71
+ name: "position",
72
+ calculate: (data, field, item)=>({lat: item.latitude, lng: item.longitude})
73
+ }],
74
+ </pre>
75
+
76
+ As you can see, _title_ is mapped to the existing feed value _humanReadableLocation_, and _position_ is
77
+ calculated by returning an object with _lat_ and _lng_ set to the corresponding values from the feed.
78
+
79
+ Save and refresh _earthquakes_. You can use the debugger to inspect the store via _Shift-Ctrl-right-click_ and
80
+ putting the main view into a global variable. Then run
81
+
82
+ temp1.getModel().stores.earthquakes.items
83
+
84
+ Look at one of the items and you should see that _title_ and _location_ are in each record.
85
+
86
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/StoreHasTitleAndLocation.png"></img>
87
+
88
+ </details>
89
+
90
+ <details>
91
+ <summary>Use the Google Map Component</summary>
92
+
93
+ We're going to replace the top table with a Google Map. To do that we need to import the Google Maps component
94
+ and show it implace of the top table. The map should be centered on Iceland. To wit
95
+
96
+ <pre>
97
+ {
98
+ module: GoogleMapsComponent,
99
+ flex: 1,
100
+ center: {
101
+ lat: 64.8014187,
102
+ lng: -18.3096357
103
+ },
104
+ zoom: 6
105
+ }
106
+ </pre>
107
+
108
+ If we replace the top table with the map, `view/MainView.mjs` ends up with this content.
109
+
110
+ <pre data-javascript>
111
+
112
+ import Container from '../container/Base.mjs';
113
+ import Controller from './MainViewController.mjs';
114
+ import EarthquakesTable from './earthquakes/Table.mjs';
115
+ import GoogleMapsComponent from '../component/wrapper/GoogleMaps.mjs';
116
+ import ViewModel from './MainViewModel.mjs';
117
+
118
+ class MainView extends Container {
119
+ static config = {
120
+ className: 'Earthquakes.view.MainView',
121
+ ntype: 'earthquakes-main',
122
+ controller: {module: Controller},
123
+ model: {
124
+ module: ViewModel
125
+ },
126
+
127
+ layout: { ntype: 'vbox', align: 'stretch' },
128
+ items: [{
129
+ module: GoogleMapsComponent,
130
+ flex: 1,
131
+ center: {
132
+ lat: 64.8014187,
133
+ lng: -18.3096357
134
+ },
135
+ zoom: 6
136
+ },{
137
+ module: EarthquakesTable,
138
+ bind: {
139
+ store: 'stores.earthquakes'
140
+ },
141
+ style: {width: '100%'},
142
+ wrapperStyle: {
143
+ height: 'auto' // Because neo-table-wrapper sets height:'100%', which it probably shouldn't
144
+ }
145
+ }],
146
+ }
147
+ }
148
+
149
+ Neo.setupClass(MainView);
150
+
151
+ export default MainView;
152
+ </pre>
153
+
154
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/CenteredMap.png"></img>
155
+
156
+
157
+ </details>
158
+
159
+ <details>
160
+ <summary>Show the markers</summary>
161
+
162
+ The markers are shown by setting up the marker store, which is a regular store whose records must contain
163
+ _location_ and _title_. We assign the store using a `bind`, just like we did with the tables.
164
+
165
+ Add this config to the map.
166
+
167
+ <pre data-javascript>
168
+ bind: {
169
+ markerStore: 'stores.earthquakes'
170
+ },
171
+ </pre>
172
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/InitialMapWithMarkers.png"></img>
173
+
174
+ </details>
175
+
@@ -0,0 +1,38 @@
1
+ In this lab you'll set up an event handler for the table and map.
2
+
3
+ <details>
4
+ <summary>Add a listener to the table</summary>
5
+
6
+ Tables fire a select event, passing an object that contains a reference to the corresponding row.
7
+
8
+ Add this table config:
9
+
10
+ listeners: {
11
+ select: (data) => console.log(data.record)
12
+ }
13
+
14
+ Save and refresh, then click on a table row. If you look at the debugger console you'll see the record being logged.
15
+
16
+ Just for fun, expand the logged value and look for the size property. If you recall, that's a value from the feed, and one of the things we configured in the store's fields:[].
17
+
18
+ In the console, click on the ellipses by size and enter a new value, like 2.5. (Don't enter a larger value, or you may destroy that part of Iceland.)
19
+
20
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/LogTableClick.png"></img>
21
+
22
+ After changing the value you should immediately see it reflected in the table row.
23
+
24
+ </details>
25
+
26
+ <details>
27
+ <summary>Add a listener to a map event
28
+ </summary>
29
+
30
+ Now add a `markerClick` listener to the Google Map.
31
+
32
+ listeners: {
33
+ markerClick: data => console.log(data.data.record)
34
+ },
35
+
36
+ Save, refresh, and confirm that you see the value logged when you click on a map marker.
37
+
38
+ </details>
@@ -532,7 +532,7 @@ class MainView extends Base {
532
532
  type: "Date",
533
533
  }],
534
534
  },
535
- url: "https://apis.is/earthquake/is",
535
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
536
536
  responseRoot: "results",
537
537
  autoLoad: true,
538
538
  },
@@ -604,7 +604,7 @@ Here's the config for the store.
604
604
  type: "Date",
605
605
  }],
606
606
  },
607
- url: "https://apis.is/earthquake/is",
607
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
608
608
  responseRoot: "results",
609
609
  autoLoad: true,
610
610
  }
@@ -773,7 +773,7 @@ static config = {
773
773
  type: "Date",
774
774
  }],
775
775
  },
776
- url: "https://apis.is/earthquake/is",
776
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
777
777
  responseRoot: "results",
778
778
  autoLoad: true,
779
779
  },
@@ -896,7 +896,7 @@ class MainView extends Base {
896
896
  type: "Date"
897
897
  }]
898
898
  },
899
- url: "https://apis.is/earthquake/is",
899
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
900
900
  responseRoot: "results",
901
901
  autoLoad: true
902
902
  },
@@ -920,7 +920,7 @@ class MainView extends Base {
920
920
  type: "Date"
921
921
  }]
922
922
  },
923
- url: "https://apis.is/earthquake/is",
923
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
924
924
  responseRoot: "results",
925
925
  autoLoad: true
926
926
  },
@@ -939,7 +939,7 @@ class MainView extends Base {
939
939
  type: "Date"
940
940
  }]
941
941
  },
942
- url: "https://apis.is/earthquake/is",
942
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
943
943
  responseRoot: "results",
944
944
  autoLoad: true
945
945
  },
@@ -1012,7 +1012,7 @@ class MainView extends Base {
1012
1012
  type: "Date"
1013
1013
  }]
1014
1014
  },
1015
- url: "https://apis.is/earthquake/is",
1015
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
1016
1016
  responseRoot: "results",
1017
1017
  autoLoad: true
1018
1018
  },
@@ -1092,7 +1092,7 @@ class MainViewModel extends Model {
1092
1092
  type: "Date"
1093
1093
  }]
1094
1094
  },
1095
- url: "https://apis.is/earthquake/is",
1095
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
1096
1096
  responseRoot: "results",
1097
1097
  autoLoad: true
1098
1098
  },
@@ -1298,10 +1298,10 @@ If we replace the top table with the map, `view/MainView.mjs` ends up with this
1298
1298
 
1299
1299
  <pre data-javascript>
1300
1300
 
1301
- import Container from '../../../node_modules/neo.mjs/src/container/Base.mjs';
1301
+ import Container from '../container/Base.mjs';
1302
1302
  import Controller from './MainViewController.mjs';
1303
1303
  import EarthquakesTable from './earthquakes/Table.mjs';
1304
- import GoogleMapsComponent from '../../../src/component/wrapper/GoogleMaps.mjs';
1304
+ import GoogleMapsComponent from '../component/wrapper/GoogleMaps.mjs';
1305
1305
  import ViewModel from './MainViewModel.mjs';
1306
1306
 
1307
1307
  class MainView extends Container {
@@ -14,8 +14,8 @@ The code below shows two text fields, with `listeners` for `change` and `focusEn
14
14
  (The events for any component are documented in the API docs.)
15
15
 
16
16
  <pre data-neo>
17
- import Container from '../../../../src/container/Base.mjs';
18
- import TextField from '../../../../src/form/field/Text.mjs';
17
+ import Container from '../container/Base.mjs';
18
+ import TextField from '../form/field/Text.mjs';
19
19
 
20
20
  class MainView extends Container {
21
21
  static config = {
@@ -53,7 +53,7 @@ A `Neo.controller.Component` is a simple class associated with a component class
53
53
  instance of its associated controller is automatically created.
54
54
 
55
55
  <pre data-neo>
56
- import Base from '../../../../src/controller/Component.mjs';
56
+ import Base from '../controller/Component.mjs';
57
57
 
58
58
  class MainViewController extends Base {
59
59
  static config = {
@@ -67,8 +67,8 @@ class MainViewController extends Base {
67
67
  Neo.setupClass(MainViewController);
68
68
 
69
69
 
70
- import Container from '../../../../src/container/Base.mjs';
71
- import TextField from '../../../../src/form/field/Text.mjs';
70
+ import Container from '../container/Base.mjs';
71
+ import TextField from '../form/field/Text.mjs';
72
72
 
73
73
  class MainView extends Container {
74
74
  static config = {
@@ -118,8 +118,8 @@ before the value is accessed. We're using the _after_ method to fire a `change`
118
118
 
119
119
 
120
120
  <pre data-neo>
121
- import Button from '../../../../src/button/Base.mjs';
122
- import Container from '../../../../src/container/Base.mjs';
121
+ import Button from '../button/Base.mjs';
122
+ import Container from '../container/Base.mjs';
123
123
 
124
124
  class ToggleButton extends Button {
125
125
  static config = {
@@ -6,9 +6,9 @@ to test.
6
6
  Consider this code. It's a panel with a header and a table. The table has a store.
7
7
 
8
8
  <pre data-neo>
9
- import Button from '../../../../src/button/Base.mjs';
10
- import Panel from '../../../../src/container/Panel.mjs';
11
- import Table from '../../../../src/table/Container.mjs';
9
+ import Button from '../button/Base.mjs';
10
+ import Panel from '../container/Panel.mjs';
11
+ import Table from '../table/Container.mjs';
12
12
 
13
13
  class MainView extends Panel {
14
14
  static config = {
@@ -49,10 +49,10 @@ have been refactored into their own classes, and the main view is using them. Th
49
49
  more abstract, and each class can be reused, tested, and maintained independently.
50
50
 
51
51
  <pre data-neo>
52
- import Button from '../../../../src/button/Base.mjs';
53
- import Panel from '../../../../src/container/Panel.mjs';
54
- import Store from '../../../../src/data/Store.mjs';
55
- import Table from '../../../../src/table/Container.mjs';
52
+ import Button from '../button/Base.mjs';
53
+ import Panel from '../container/Panel.mjs';
54
+ import Store from '../data/Store.mjs';
55
+ import Table from '../table/Container.mjs';
56
56
 
57
57
  class BeatlesButton extends Button {
58
58
  static config = {
File without changes