neo.mjs 7.0.5 → 7.1.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 (79) hide show
  1. package/README.md +2 -2
  2. package/apps/ServiceWorker.mjs +2 -2
  3. package/apps/portal/index.html +1 -1
  4. package/apps/portal/model/Content.mjs +3 -2
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  6. package/apps/portal/view/home/parts/Features.mjs +5 -5
  7. package/examples/ServiceWorker.mjs +2 -2
  8. package/examples/todoList/version2/MainContainer.mjs +38 -48
  9. package/examples/todoList/version2/TodoList.mjs +6 -39
  10. package/examples/todoList/version2/TodoListModel.mjs +1 -1
  11. package/package.json +7 -7
  12. package/resources/data/deck/learnneo/pages/Welcome.md +0 -4
  13. package/resources/data/deck/learnneo/pages/guides/Forms.md +1 -0
  14. package/resources/data/deck/learnneo/pages/guides/InstanceLifecycle.md +1 -0
  15. package/resources/data/deck/learnneo/pages/guides/Layouts.md +1 -0
  16. package/resources/data/deck/learnneo/pages/guides/Mixins.md +1 -0
  17. package/resources/data/deck/learnneo/pages/guides/MultiWindow.md +1 -0
  18. package/resources/data/deck/learnneo/pages/guides/Tables.md +1 -0
  19. package/resources/data/deck/learnneo/pages/guides/events/DomEvents.md +263 -0
  20. package/resources/data/deck/learnneo/pages/{TodoList.md → tutorials/TodoList.md} +23 -33
  21. package/resources/data/deck/learnneo/tree.json +45 -48
  22. package/resources/scss/src/component/Splitter.scss +10 -1
  23. package/resources/scss/theme-dark/component/Splitter.scss +6 -3
  24. package/resources/scss/theme-light/component/Splitter.scss +6 -3
  25. package/resources/scss/theme-neo-light/component/Splitter.scss +6 -20
  26. package/src/DefaultConfig.mjs +2 -2
  27. package/src/code/LivePreview.mjs +28 -1
  28. package/src/collection/Base.mjs +1 -0
  29. package/src/component/Splitter.mjs +22 -4
  30. package/src/controller/Component.mjs +10 -10
  31. package/src/core/Observable.mjs +4 -27
  32. package/src/draggable/DragZone.mjs +6 -5
  33. package/src/grid/View.mjs +82 -0
  34. package/src/list/Base.mjs +16 -0
  35. package/src/main/addon/DragDrop.mjs +3 -2
  36. package/src/manager/DomEvent.mjs +30 -18
  37. package/src/table/View.mjs +82 -0
  38. package/src/util/Function.mjs +24 -0
  39. package/resources/data/deck/learnneo/pages/2023-10-08T20-37-30-658Z.md +0 -0
  40. package/resources/data/deck/learnneo/pages/2023-10-08T22-22-11-013Z.md +0 -0
  41. package/resources/data/deck/learnneo/pages/Earthquakes-01-goals.md +0 -32
  42. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-01-generate-a-workspace.md +0 -47
  43. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-02-generate-the-starter-app.md +0 -150
  44. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-03-debugging.md +0 -136
  45. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-04-fetch-data.md +0 -146
  46. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-05-refactor-the-table.md +0 -146
  47. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-06-use-a-view-model.md +0 -301
  48. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-07-use-the-google-maps-addon.md +0 -175
  49. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-08-events.md +0 -38
  50. package/resources/data/deck/learnneo/pages/TestLivePreview.md +0 -32
  51. package/resources/data/deck/learnneo/pages/WhatAboutHTML.md +0 -1
  52. package/resources/data/deck/learnneo/pages/stylesheet.md +0 -57
  53. /package/resources/data/deck/learnneo/pages/{WhyNeo-Effort.md → benefits/Effort.md} +0 -0
  54. /package/resources/data/deck/learnneo/pages/{WhyNeo-Features.md → benefits/Features.md} +0 -0
  55. /package/resources/data/deck/learnneo/pages/{WhyNeo-Intro.md → benefits/Introduction.md} +0 -0
  56. /package/resources/data/deck/learnneo/pages/{WhyNeo-Multi-Window.md → benefits/Multi-Window.md} +0 -0
  57. /package/resources/data/deck/learnneo/pages/{WhyNeo-Quick.md → benefits/Quick.md} +0 -0
  58. /package/resources/data/deck/learnneo/pages/{WhyNeo-Speed.md → benefits/Speed.md} +0 -0
  59. /package/resources/data/deck/learnneo/pages/{ComponentModels.md → gettingstarted/ComponentModels.md} +0 -0
  60. /package/resources/data/deck/learnneo/pages/{Config.md → gettingstarted/Config.md} +0 -0
  61. /package/resources/data/deck/learnneo/pages/{DescribingTheUI.md → gettingstarted/DescribingTheUI.md} +0 -0
  62. /package/resources/data/deck/learnneo/pages/{Events.md → gettingstarted/Events.md} +0 -0
  63. /package/resources/data/deck/learnneo/pages/{Extending.md → gettingstarted/Extending.md} +0 -0
  64. /package/resources/data/deck/learnneo/pages/{References.md → gettingstarted/References.md} +0 -0
  65. /package/resources/data/deck/learnneo/pages/{Setup.md → gettingstarted/Setup.md} +0 -0
  66. /package/resources/data/deck/learnneo/pages/{2023-10-14T19-25-08-153Z.md → gettingstarted/Workspaces.md} +0 -0
  67. /package/resources/data/deck/learnneo/pages/{ComponentsAndContainers.md → guides/ComponentsAndContainers.md} +0 -0
  68. /package/resources/data/deck/learnneo/pages/{CustomComponents.md → guides/CustomComponents.md} +0 -0
  69. /package/resources/data/deck/learnneo/pages/{MainThreadAddonExample.md → guides/MainThreadAddonExample.md} +0 -0
  70. /package/resources/data/deck/learnneo/pages/{MainThreadAddonIntro.md → guides/MainThreadAddonIntro.md} +0 -0
  71. /package/resources/data/deck/learnneo/pages/{GuideViewModels.md → guides/ViewModels.md} +0 -0
  72. /package/resources/data/deck/learnneo/pages/{GuideEvents.md → guides/events/CustomEvents.md} +0 -0
  73. /package/resources/data/deck/learnneo/pages/{2023-10-08T20-20-37-336Z.md → javascript/ClassFeatures.md} +0 -0
  74. /package/resources/data/deck/learnneo/pages/{2023-10-07T19-18-28-517Z.md → javascript/Classes.md} +0 -0
  75. /package/resources/data/deck/learnneo/pages/{2023-10-31T13-59-37-550Z.md → javascript/NewNode.md} +0 -0
  76. /package/resources/data/deck/learnneo/pages/{2023-10-08T20-20-07-934Z.md → javascript/Overrides.md} +0 -0
  77. /package/resources/data/deck/learnneo/pages/{2023-10-08T21-58-25-809Z.md → javascript/Super.md} +0 -0
  78. /package/resources/data/deck/learnneo/pages/{Earthquakes.md → tutorials/Earthquakes.md} +0 -0
  79. /package/resources/data/deck/learnneo/pages/{RSP.md → tutorials/RSP.md} +0 -0
package/src/grid/View.mjs CHANGED
@@ -23,6 +23,27 @@ class View extends Component {
23
23
  baseCls: ['neo-grid-view']
24
24
  }
25
25
 
26
+ /**
27
+ * @param config
28
+ */
29
+ construct(config) {
30
+ super.construct(config);
31
+
32
+ let me = this;
33
+
34
+ me.addDomListeners([{
35
+ click : me.onCellClick,
36
+ dblclick: me.onCellDoubleClick,
37
+ delegate: '.neo-grid-cell',
38
+ scope : me
39
+ }, {
40
+ click : me.onRowClick,
41
+ dblclick: me.onRowDoubleClick,
42
+ delegate: '.neo-grid-row',
43
+ scope : me
44
+ }])
45
+ }
46
+
26
47
  /**
27
48
  * @param {Array} inputData
28
49
  */
@@ -169,6 +190,39 @@ class View extends Component {
169
190
  super.destroy(updateParentVdom, silent)
170
191
  }
171
192
 
193
+ /**
194
+ * @param {Object} data
195
+ * @param {String} eventName
196
+ */
197
+ fireCellEvent(data, eventName) {
198
+ let me = this,
199
+ {id} = data.target,
200
+ dataField = me.getCellDataField(id),
201
+ record = me.getRecord(id);
202
+
203
+ me.parent.fire(eventName, {id: me, data, dataField, record})
204
+ }
205
+
206
+ /**
207
+ * @param {Object} data
208
+ * @param {String} eventName
209
+ */
210
+ fireRowEvent(data, eventName) {
211
+ let me = this,
212
+ {id} = data.target,
213
+ record = me.getRecord(id);
214
+
215
+ me.parent.fire(eventName, {id: me, data, record})
216
+ }
217
+
218
+ /**
219
+ * @param {String} cellId
220
+ * @returns {String}
221
+ */
222
+ getCellDataField(cellId) {
223
+ return cellId.split('__')[2]
224
+ }
225
+
172
226
  /**
173
227
  * @param {Object} record
174
228
  * @param {String} field
@@ -232,6 +286,34 @@ class View extends Component {
232
286
  return ['neo-grid-row']
233
287
  }
234
288
 
289
+ /**
290
+ * @param {Object} data
291
+ */
292
+ onCellClick(data) {
293
+ this.fireCellEvent(data, 'cellClick')
294
+ }
295
+
296
+ /**
297
+ * @param {Object} data
298
+ */
299
+ onCellDoubleClick(data) {
300
+ this.fireCellEvent(data, 'cellDoubleClick')
301
+ }
302
+
303
+ /**
304
+ * @param {Object} data
305
+ */
306
+ onRowClick(data) {
307
+ this.fireRowEvent(data, 'rowClick')
308
+ }
309
+
310
+ /**
311
+ * @param {Object} data
312
+ */
313
+ onRowDoubleClick(data) {
314
+ this.fireRowEvent(data, 'rowDoubleClick')
315
+ }
316
+
235
317
  /**
236
318
  * Gets triggered after changing the value of a record field.
237
319
  * E.g. myRecord.foo = 'bar';
package/src/list/Base.mjs CHANGED
@@ -409,6 +409,22 @@ class Base extends Component {
409
409
  me.cls = cls
410
410
  }
411
411
 
412
+ /**
413
+ * Triggered after the windowId config got changed
414
+ * @param {Number} value
415
+ * @param {Number} oldValue
416
+ * @protected
417
+ */
418
+ afterSetWindowId(value, oldValue) {
419
+ super.afterSetWindowId(value, oldValue);
420
+
421
+ let {navigator} = this;
422
+
423
+ if (navigator) {
424
+ navigator.windowId = value
425
+ }
426
+ }
427
+
412
428
  /**
413
429
  * Triggered before the selectionModel config gets changed.
414
430
  * @param {Neo.selection.Model} value
@@ -153,8 +153,8 @@ class DragDrop extends Base {
153
153
 
154
154
  Promise.all(imports).then(modules => {
155
155
  // create the Mouse- and / or TouchSensor
156
- Neo.create({
157
- module: modules[0].default
156
+ modules.forEach(module => {
157
+ Neo.create({module: module.default})
158
158
  })
159
159
  })
160
160
  }
@@ -461,6 +461,7 @@ class DragDrop extends Base {
461
461
  node;
462
462
 
463
463
  delete data.appName;
464
+ delete data.windowId;
464
465
 
465
466
  if (data.boundaryContainerId) {
466
467
  node = DomAccess.getElementOrBody(data.boundaryContainerId);
@@ -1,9 +1,10 @@
1
- import Base from '../core/Base.mjs';
2
- import ComponentManager from './Component.mjs';
3
- import FocusManager from './Focus.mjs';
4
- import Logger from '../util/Logger.mjs';
5
- import NeoArray from '../util/Array.mjs';
6
- import VDomUtil from '../util/VDom.mjs';
1
+ import Base from '../core/Base.mjs';
2
+ import ComponentManager from './Component.mjs';
3
+ import FocusManager from './Focus.mjs';
4
+ import Logger from '../util/Logger.mjs';
5
+ import NeoArray from '../util/Array.mjs';
6
+ import {resolveCallback} from '../util/Function.mjs';
7
+ import VDomUtil from '../util/VDom.mjs';
7
8
 
8
9
  const eventConfigKeys = [
9
10
  'bubble',
@@ -192,10 +193,12 @@ class DomEvent extends Base {
192
193
  */
193
194
  generateListenerConfig(config, scope) {
194
195
  return {
196
+ bubble : config.bubble,
195
197
  delegate : config.delegate,
196
198
  eventName: config.eventName,
197
199
  id : scope.id,
198
200
  opts : config,
201
+ priority : config.priority,
199
202
  scope : config.scope || scope,
200
203
  vnodeId : config.vnodeId || scope.id
201
204
  };
@@ -217,6 +220,7 @@ class DomEvent extends Base {
217
220
 
218
221
  /**
219
222
  * @param {Object} config
223
+ * @param {Boolean} config.bubble
220
224
  * @param {String} config.delegate
221
225
  * @param {String} config.eventName
222
226
  * @param {String} config.id
@@ -264,9 +268,10 @@ class DomEvent extends Base {
264
268
 
265
269
  if (localEvents.length > 0) {
266
270
  Neo.worker.App.promiseMessage('main', {
267
- action : 'addDomListener',
268
- appName: component.appName,
269
- events : localEvents
271
+ action : 'addDomListener',
272
+ appName : component.appName,
273
+ events : localEvents,
274
+ windowId: component.windowId
270
275
  }).then(data => {
271
276
  // console.log('added domListener', data);
272
277
  }).catch(err => {
@@ -278,6 +283,7 @@ class DomEvent extends Base {
278
283
 
279
284
  /**
280
285
  * @param {Object} config
286
+ * @param {Boolean} config.bubble
281
287
  * @param {String} config.delegate
282
288
  * @param {String} config.eventName
283
289
  * @param {String} config.id
@@ -285,20 +291,23 @@ class DomEvent extends Base {
285
291
  * @param {Number} config.opts
286
292
  * @param {Number} config.originalConfig
287
293
  * @param {String} config.ownerId
288
- * @param {Number} config.priority
294
+ * @param {Number} config.priority=1
289
295
  * @param {Object} config.scope
290
296
  * @param {String} config.vnodeId
291
297
  * @returns {Boolean} true if the listener got registered successfully (false in case it was already there)
292
298
  */
293
299
  register(config) {
294
- let alreadyRegistered = false,
300
+ let me = this,
301
+ alreadyRegistered = false,
295
302
  {eventName, id, opts, scope} = config,
296
- listeners = this.items,
303
+ listeners = me.items,
297
304
  fnType = typeof opts,
298
305
  fn, listener, listenerConfig, listenerId;
299
306
 
300
- if (fnType === 'function' || fnType === 'string') {
307
+ if (fnType === 'function') {
301
308
  fn = opts
309
+ } else if (fnType === 'string') {
310
+ fn = resolveCallback(opts, scope).fn
302
311
  } else {
303
312
  fn = opts.fn;
304
313
  scope = opts.scope || scope
@@ -343,22 +352,23 @@ class DomEvent extends Base {
343
352
  mounted : !config.local && globalDomEvents.includes(eventName),
344
353
  originalConfig: config.originalConfig,
345
354
  ownerId : config.ownerId,
346
- priority : config.priority || 1,
355
+ priority : config.priority || opts.priority || 1,
347
356
  scope,
348
357
  vnodeId : config.vnodeId
349
358
  };
350
359
 
351
- this.map[listenerId] = listenerConfig;
360
+ me.map[listenerId] = listenerConfig;
352
361
 
353
362
  listeners[id][eventName].push(listenerConfig);
354
363
 
355
- listeners[id][eventName].sort((a, b) => a.priority > b.priority);
364
+ listeners[id][eventName].sort((a, b) => b.priority - a.priority);
356
365
 
357
366
  return true
358
367
  }
359
368
 
360
369
  /**
361
370
  * @param {Object} config
371
+ * @param {Boolean} config.bubble
362
372
  * @param {String} config.eventName
363
373
  * @param {String} config.id
364
374
  * @param {Object} config.opts
@@ -414,14 +424,16 @@ class DomEvent extends Base {
414
424
  Object.entries(domListener).forEach(([key, value]) => {
415
425
  if (!eventConfigKeys.includes(key)) {
416
426
  me.register({
427
+ bubble : domListener.bubble || value.bubble,
417
428
  delegate : domListener.delegate || value.delegate || '#' + component.id,
418
429
  eventName : key,
419
430
  id : component.id,
420
431
  opts : value,
421
432
  originalConfig: domListener,
422
433
  ownerId : component.id,
423
- scope : domListener.scope || component,
424
- vnodeId : domListener.vnodeId || value.vnodeId || component.id
434
+ priority : domListener.priority || value.priority || 1,
435
+ scope : domListener.scope || component,
436
+ vnodeId : domListener.vnodeId || value.vnodeId || component.id
425
437
  })
426
438
  }
427
439
  })
@@ -50,6 +50,27 @@ class View extends Component {
50
50
  {tag: 'tbody', cn: []}
51
51
  }
52
52
 
53
+ /**
54
+ * @param config
55
+ */
56
+ construct(config) {
57
+ super.construct(config);
58
+
59
+ let me = this;
60
+
61
+ me.addDomListeners([{
62
+ click : me.onCellClick,
63
+ dblclick: me.onCellDoubleClick,
64
+ delegate: '.neo-table-cell',
65
+ scope : me
66
+ }, {
67
+ click : me.onRowClick,
68
+ dblclick: me.onRowDoubleClick,
69
+ delegate: '.neo-table-row',
70
+ scope : me
71
+ }])
72
+ }
73
+
53
74
  /**
54
75
  * @param {Object} data
55
76
  * @param {String} [data.cellId]
@@ -246,6 +267,39 @@ class View extends Component {
246
267
  super.destroy(...args)
247
268
  }
248
269
 
270
+ /**
271
+ * @param {Object} data
272
+ * @param {String} eventName
273
+ */
274
+ fireCellEvent(data, eventName) {
275
+ let me = this,
276
+ {id} = data.target,
277
+ dataField = me.getCellDataField(id),
278
+ record = me.getRecord(id);
279
+
280
+ me.parent.fire(eventName, {id: me, data, dataField, record})
281
+ }
282
+
283
+ /**
284
+ * @param {Object} data
285
+ * @param {String} eventName
286
+ */
287
+ fireRowEvent(data, eventName) {
288
+ let me = this,
289
+ {id} = data.target,
290
+ record = me.getRecord(id);
291
+
292
+ me.parent.fire(eventName, {id: me, data, record})
293
+ }
294
+
295
+ /**
296
+ * @param {String} cellId
297
+ * @returns {String}
298
+ */
299
+ getCellDataField(cellId) {
300
+ return cellId.split('__')[2]
301
+ }
302
+
249
303
  /**
250
304
  * @param {Object} record
251
305
  * @param {String} dataField
@@ -341,6 +395,34 @@ class View extends Component {
341
395
  return ['neo-table-row']
342
396
  }
343
397
 
398
+ /**
399
+ * @param {Object} data
400
+ */
401
+ onCellClick(data) {
402
+ this.fireCellEvent(data, 'cellClick')
403
+ }
404
+
405
+ /**
406
+ * @param {Object} data
407
+ */
408
+ onCellDoubleClick(data) {
409
+ this.fireCellEvent(data, 'cellDoubleClick')
410
+ }
411
+
412
+ /**
413
+ * @param {Object} data
414
+ */
415
+ onRowClick(data) {
416
+ this.fireRowEvent(data, 'rowClick')
417
+ }
418
+
419
+ /**
420
+ * @param {Object} data
421
+ */
422
+ onRowDoubleClick(data) {
423
+ this.fireRowEvent(data, 'rowDoubleClick')
424
+ }
425
+
344
426
  /**
345
427
  * Gets triggered after changing the value of a record field.
346
428
  * E.g. myRecord.foo = 'bar';
@@ -123,6 +123,30 @@ export function intercept(target, targetMethodName, interceptFunction, scope, pr
123
123
  })
124
124
  }
125
125
 
126
+ /**
127
+ * Locate a callable function by name in the passed scope.
128
+ *
129
+ * If the name starts with 'up.', the parent Component chain is searched.
130
+ *
131
+ * This is used by manager.DomEvents & core.Observable.fire and by 'handler' function calls to resolve
132
+ * string function names in the Component's own hierarchy.
133
+ * @param {Function|String} fn A function, or the name of a function to find in the passed scope object/
134
+ * @param {Object} scope=this The scope to find the function in if it is specified as a string.
135
+ * @returns {Object}
136
+ */
137
+ export function resolveCallback(fn, scope=this) {
138
+ if (typeof fn === 'string') {
139
+ if (!scope[fn] && fn.startsWith('up.')) {
140
+ fn = fn.slice(3);
141
+ while (!scope[fn] && (scope = scope.parent));
142
+ }
143
+
144
+ fn = scope[fn]
145
+ }
146
+
147
+ return {fn, scope}
148
+ }
149
+
126
150
  /**
127
151
  * @param {Function} callback
128
152
  * @param {Neo.core.Base} scope
@@ -1,32 +0,0 @@
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
-
@@ -1,47 +0,0 @@
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>
@@ -1,150 +0,0 @@
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>