neo.mjs 7.0.6 → 7.2.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 (103) 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/model/Example.mjs +46 -0
  6. package/apps/portal/resources/data/examples_devmode.json +173 -0
  7. package/apps/portal/resources/data/examples_dist_dev.json +164 -0
  8. package/apps/portal/resources/data/examples_dist_prod.json +164 -0
  9. package/apps/portal/store/Examples.mjs +33 -0
  10. package/apps/portal/view/HeaderToolbar.mjs +3 -0
  11. package/apps/portal/view/Viewport.mjs +3 -2
  12. package/apps/portal/view/ViewportController.mjs +32 -14
  13. package/apps/portal/view/about/Container.mjs +2 -2
  14. package/apps/portal/view/blog/List.mjs +3 -3
  15. package/apps/portal/view/examples/List.mjs +122 -0
  16. package/apps/portal/view/examples/TabContainer.mjs +79 -0
  17. package/apps/portal/view/examples/TabContainerController.mjs +41 -0
  18. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  19. package/apps/portal/view/home/parts/Features.mjs +5 -5
  20. package/apps/shareddialog/view/MainContainerController.mjs +37 -30
  21. package/examples/ServiceWorker.mjs +2 -2
  22. package/examples/todoList/version2/MainContainer.mjs +38 -48
  23. package/examples/todoList/version2/TodoList.mjs +6 -39
  24. package/examples/todoList/version2/TodoListModel.mjs +2 -2
  25. package/package.json +7 -7
  26. package/resources/data/deck/learnneo/pages/Welcome.md +0 -4
  27. package/resources/data/deck/learnneo/pages/guides/Forms.md +1 -0
  28. package/resources/data/deck/learnneo/pages/guides/InstanceLifecycle.md +1 -0
  29. package/resources/data/deck/learnneo/pages/guides/Layouts.md +1 -0
  30. package/resources/data/deck/learnneo/pages/guides/Mixins.md +1 -0
  31. package/resources/data/deck/learnneo/pages/guides/MultiWindow.md +1 -0
  32. package/resources/data/deck/learnneo/pages/guides/Tables.md +1 -0
  33. package/resources/data/deck/learnneo/pages/guides/events/DomEvents.md +263 -0
  34. package/resources/data/deck/learnneo/pages/{TodoList.md → tutorials/TodoList.md} +23 -33
  35. package/resources/data/deck/learnneo/tree.json +45 -48
  36. package/resources/scss/src/apps/portal/about/MemberContainer.scss +5 -4
  37. package/resources/scss/src/apps/portal/blog/Container.scss +2 -0
  38. package/resources/scss/src/apps/portal/examples/List.scss +157 -0
  39. package/resources/scss/src/apps/portal/examples/TabContainer.scss +36 -0
  40. package/resources/scss/src/button/Base.scss +4 -0
  41. package/resources/scss/src/calendar/view/calendars/ColorsList.scss +4 -0
  42. package/resources/scss/src/calendar/view/calendars/List.scss +2 -1
  43. package/resources/scss/src/dialog/Base.scss +2 -2
  44. package/resources/scss/src/list/Base.scss +3 -3
  45. package/resources/scss/theme-neo-light/button/Base.scss +3 -3
  46. package/src/DefaultConfig.mjs +2 -2
  47. package/src/calendar/view/MainContainer.mjs +7 -5
  48. package/src/calendar/view/calendars/EditContainer.mjs +3 -2
  49. package/src/calendar/view/calendars/List.mjs +1 -0
  50. package/src/calendar/view/week/Component.mjs +5 -3
  51. package/src/calendar/view/week/plugin/DragDrop.mjs +4 -3
  52. package/src/code/LivePreview.mjs +28 -1
  53. package/src/collection/Base.mjs +1 -0
  54. package/src/controller/Component.mjs +10 -10
  55. package/src/core/Observable.mjs +4 -27
  56. package/src/grid/View.mjs +82 -0
  57. package/src/layout/Cube.mjs +44 -38
  58. package/src/list/Base.mjs +16 -0
  59. package/src/main/addon/DragDrop.mjs +3 -2
  60. package/src/manager/DomEvent.mjs +30 -18
  61. package/src/table/View.mjs +82 -0
  62. package/src/util/Function.mjs +24 -0
  63. package/resources/data/deck/learnneo/pages/2023-10-08T20-37-30-658Z.md +0 -0
  64. package/resources/data/deck/learnneo/pages/2023-10-08T22-22-11-013Z.md +0 -0
  65. package/resources/data/deck/learnneo/pages/Earthquakes-01-goals.md +0 -32
  66. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-01-generate-a-workspace.md +0 -47
  67. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-02-generate-the-starter-app.md +0 -150
  68. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-03-debugging.md +0 -136
  69. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-04-fetch-data.md +0 -146
  70. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-05-refactor-the-table.md +0 -146
  71. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-06-use-a-view-model.md +0 -301
  72. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-07-use-the-google-maps-addon.md +0 -175
  73. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-08-events.md +0 -38
  74. package/resources/data/deck/learnneo/pages/TestLivePreview.md +0 -32
  75. package/resources/data/deck/learnneo/pages/WhatAboutHTML.md +0 -1
  76. package/resources/data/deck/learnneo/pages/stylesheet.md +0 -57
  77. /package/resources/data/deck/learnneo/pages/{WhyNeo-Effort.md → benefits/Effort.md} +0 -0
  78. /package/resources/data/deck/learnneo/pages/{WhyNeo-Features.md → benefits/Features.md} +0 -0
  79. /package/resources/data/deck/learnneo/pages/{WhyNeo-Intro.md → benefits/Introduction.md} +0 -0
  80. /package/resources/data/deck/learnneo/pages/{WhyNeo-Multi-Window.md → benefits/Multi-Window.md} +0 -0
  81. /package/resources/data/deck/learnneo/pages/{WhyNeo-Quick.md → benefits/Quick.md} +0 -0
  82. /package/resources/data/deck/learnneo/pages/{WhyNeo-Speed.md → benefits/Speed.md} +0 -0
  83. /package/resources/data/deck/learnneo/pages/{ComponentModels.md → gettingstarted/ComponentModels.md} +0 -0
  84. /package/resources/data/deck/learnneo/pages/{Config.md → gettingstarted/Config.md} +0 -0
  85. /package/resources/data/deck/learnneo/pages/{DescribingTheUI.md → gettingstarted/DescribingTheUI.md} +0 -0
  86. /package/resources/data/deck/learnneo/pages/{Events.md → gettingstarted/Events.md} +0 -0
  87. /package/resources/data/deck/learnneo/pages/{Extending.md → gettingstarted/Extending.md} +0 -0
  88. /package/resources/data/deck/learnneo/pages/{References.md → gettingstarted/References.md} +0 -0
  89. /package/resources/data/deck/learnneo/pages/{Setup.md → gettingstarted/Setup.md} +0 -0
  90. /package/resources/data/deck/learnneo/pages/{2023-10-14T19-25-08-153Z.md → gettingstarted/Workspaces.md} +0 -0
  91. /package/resources/data/deck/learnneo/pages/{ComponentsAndContainers.md → guides/ComponentsAndContainers.md} +0 -0
  92. /package/resources/data/deck/learnneo/pages/{CustomComponents.md → guides/CustomComponents.md} +0 -0
  93. /package/resources/data/deck/learnneo/pages/{MainThreadAddonExample.md → guides/MainThreadAddonExample.md} +0 -0
  94. /package/resources/data/deck/learnneo/pages/{MainThreadAddonIntro.md → guides/MainThreadAddonIntro.md} +0 -0
  95. /package/resources/data/deck/learnneo/pages/{GuideViewModels.md → guides/ViewModels.md} +0 -0
  96. /package/resources/data/deck/learnneo/pages/{GuideEvents.md → guides/events/CustomEvents.md} +0 -0
  97. /package/resources/data/deck/learnneo/pages/{2023-10-08T20-20-37-336Z.md → javascript/ClassFeatures.md} +0 -0
  98. /package/resources/data/deck/learnneo/pages/{2023-10-07T19-18-28-517Z.md → javascript/Classes.md} +0 -0
  99. /package/resources/data/deck/learnneo/pages/{2023-10-31T13-59-37-550Z.md → javascript/NewNode.md} +0 -0
  100. /package/resources/data/deck/learnneo/pages/{2023-10-08T20-20-07-934Z.md → javascript/Overrides.md} +0 -0
  101. /package/resources/data/deck/learnneo/pages/{2023-10-08T21-58-25-809Z.md → javascript/Super.md} +0 -0
  102. /package/resources/data/deck/learnneo/pages/{Earthquakes.md → tutorials/Earthquakes.md} +0 -0
  103. /package/resources/data/deck/learnneo/pages/{RSP.md → tutorials/RSP.md} +0 -0
@@ -1,5 +1,6 @@
1
- import Base from './Base.mjs';
2
- import Logger from '../util/Logger.mjs';
1
+ import Base from './Base.mjs';
2
+ import {resolveCallback} from '../util/Function.mjs';
3
+ import Logger from '../util/Logger.mjs';
3
4
 
4
5
  /**
5
6
  * @class Neo.controller.Component
@@ -71,7 +72,7 @@ class Component extends Base {
71
72
 
72
73
  /**
73
74
  * @param {String} handlerName
74
- * @param {Neo.component.Base} component
75
+ * @param {Neo.component.Base} [component]
75
76
  * @returns {Neo.controller.Component|Boolean|null}
76
77
  */
77
78
  getHandlerScope(handlerName, component) {
@@ -81,7 +82,7 @@ class Component extends Base {
81
82
  if (component) {
82
83
  // Look for ths function *name* first in the Component itself.
83
84
  // If we find it, return true so calling code knows not to continue to search.
84
- const handlerCb = component.resolveCallback(handlerName, component);
85
+ const handlerCb = resolveCallback(handlerName, component);
85
86
 
86
87
  // Handler fn is resolved in the Component or its own parent chain.
87
88
  // Return a status indicating that we do not need an early binding
@@ -91,8 +92,7 @@ class Component extends Base {
91
92
  }
92
93
 
93
94
  return Neo.isFunction(me[handlerName]) ?
94
- me : parent ?
95
- parent.getHandlerScope(handlerName) : null
95
+ me : parent?.getHandlerScope(handlerName) || null
96
96
  }
97
97
 
98
98
  /**
@@ -114,7 +114,7 @@ class Component extends Base {
114
114
  {parent} = me;
115
115
 
116
116
  if (parent) {
117
- return parent;
117
+ return parent
118
118
  }
119
119
 
120
120
  return me.component.parent?.getController() || null
@@ -238,9 +238,9 @@ class Component extends Base {
238
238
  if (eventHandler) {
239
239
  scope = me.getHandlerScope(eventHandler);
240
240
 
241
- if (!scope) {
242
- Logger.logError('Unknown domEvent handler for', eventHandler, component)
243
- } else {
241
+ // There can be string based listeners like 'up.onClick', which will resolved inside manager.DomEvents
242
+ // => Do nothing in case there is no match inside the controller hierarchy.
243
+ if (scope) {
244
244
  domListener[key] = scope[eventHandler].bind(scope)
245
245
  }
246
246
  }
@@ -1,4 +1,5 @@
1
- import Base from './Base.mjs';
1
+ import Base from './Base.mjs';
2
+ import {resolveCallback} from '../util/Function.mjs';
2
3
 
3
4
  /**
4
5
  * @class Neo.core.Observable
@@ -136,7 +137,7 @@ class Observable extends Base {
136
137
  */
137
138
  callback(fn, scope=this, args) {
138
139
  if (fn) {
139
- const handler = this.resolveCallback(fn, scope);
140
+ const handler = resolveCallback(fn, scope);
140
141
  handler.fn.apply(handler.scope, args)
141
142
  }
142
143
  }
@@ -172,7 +173,7 @@ class Observable extends Base {
172
173
 
173
174
  // Resolve function name on the scope (or me), or, if it starts with 'up.'
174
175
  // look in the ownership hierarchy from me.
175
- const cb = me.resolveCallback(handler.fn, handler.scope || me);
176
+ const cb = resolveCallback(handler.fn, handler.scope || me);
176
177
 
177
178
  // remove the listener if the scope no longer exists
178
179
  if (cb.scope && !cb.scope.id) {
@@ -338,30 +339,6 @@ class Observable extends Base {
338
339
 
339
340
  // }
340
341
 
341
- /**
342
- * Locate a callable function by name in the passed scope.
343
- *
344
- * If the name starts with 'up.', the parent Component chain is searched.
345
- *
346
- * This is used by Observable.fire and by 'handler' function calls to resolve
347
- * string function names in the Component's own hierarchy.
348
- * @param {Function|String} fn A function, or the name of a function to find in the passed scope object/
349
- * @param {Object} scope The scope to find the function in if it is specified as a string.
350
- * @returns {Object}
351
- */
352
- resolveCallback(fn, scope=this) {
353
- if (typeof fn === 'string') {
354
- if (!scope[fn] && fn.startsWith('up.')) {
355
- fn = fn.slice(3);
356
- while (!scope[fn] && (scope = scope.parent));
357
- }
358
-
359
- fn = scope[fn]
360
- }
361
-
362
- return {fn, scope}
363
- }
364
-
365
342
  /**
366
343
  * Alias for removeListener
367
344
  * @param {Object|String} name
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';
@@ -130,7 +130,7 @@ class Cube extends Card {
130
130
  * @protected
131
131
  */
132
132
  async afterSetActiveIndex(value, oldValue) {
133
- if (Neo.isNumber(value)) {
133
+ if (Neo.isNumber(value) && value < 6) {
134
134
  let me = this,
135
135
  {container} = me,
136
136
  item = container.items[value];
@@ -254,24 +254,26 @@ class Cube extends Card {
254
254
  applyChildAttributes(item, index) {
255
255
  let {wrapperCls} = item;
256
256
 
257
- wrapperCls = NeoArray.union(wrapperCls, 'neo-face', Object.keys(Cube.faces)[index]);
258
-
259
- switch(index) {
260
- case 0:
261
- case 1:
262
- wrapperCls = NeoArray.union(wrapperCls, 'neo-face-z');
263
- break;
264
- case 2:
265
- case 3:
266
- wrapperCls = NeoArray.union(wrapperCls, 'neo-face-x');
267
- break;
268
- case 4:
269
- case 5:
270
- wrapperCls = NeoArray.union(wrapperCls, 'neo-face-y');
271
- break;
272
- }
257
+ if (index < 6) {
258
+ wrapperCls = NeoArray.union(wrapperCls, 'neo-face', Object.keys(Cube.faces)[index]);
259
+
260
+ switch(index) {
261
+ case 0:
262
+ case 1:
263
+ wrapperCls = NeoArray.union(wrapperCls, 'neo-face-z');
264
+ break;
265
+ case 2:
266
+ case 3:
267
+ wrapperCls = NeoArray.union(wrapperCls, 'neo-face-x');
268
+ break;
269
+ case 4:
270
+ case 5:
271
+ wrapperCls = NeoArray.union(wrapperCls, 'neo-face-y');
272
+ break;
273
+ }
273
274
 
274
- item.wrapperCls = wrapperCls
275
+ item.wrapperCls = wrapperCls
276
+ }
275
277
  }
276
278
 
277
279
  /**
@@ -298,7 +300,7 @@ class Cube extends Card {
298
300
 
299
301
  if (me.hideInactiveCardsOnDestroy) {
300
302
  vdom.cn.forEach((item, index) => {
301
- if (index !== me.activeIndex) {
303
+ if (index < 6 && index !== me.activeIndex) {
302
304
  item.removeDom = true
303
305
  }
304
306
  })
@@ -334,8 +336,10 @@ class Cube extends Card {
334
336
 
335
337
  me.timeout(50).then(() => {
336
338
  // Important when switching from a card layout to this one
337
- container.vdom.cn[0].cn[0].cn.forEach(node => {
338
- delete node.removeDom
339
+ container.vdom.cn[0].cn[0].cn.forEach((node, index) => {
340
+ if (index < 6) {
341
+ delete node.removeDom
342
+ }
339
343
  });
340
344
 
341
345
  container.update()
@@ -352,24 +356,26 @@ class Cube extends Card {
352
356
  removeChildAttributes(item, index) {
353
357
  let {wrapperCls} = item;
354
358
 
355
- NeoArray.remove(wrapperCls, ['neo-face', Object.keys(Cube.faces)[index]]);
356
-
357
- switch(index) {
358
- case 0:
359
- case 1:
360
- NeoArray.remove(wrapperCls, 'neo-face-z');
361
- break;
362
- case 2:
363
- case 3:
364
- NeoArray.remove(wrapperCls, 'neo-face-x');
365
- break;
366
- case 4:
367
- case 5:
368
- NeoArray.remove(wrapperCls, 'neo-face-y');
369
- break;
370
- }
359
+ if (index < 6) {
360
+ NeoArray.remove(wrapperCls, ['neo-face', Object.keys(Cube.faces)[index]]);
361
+
362
+ switch(index) {
363
+ case 0:
364
+ case 1:
365
+ NeoArray.remove(wrapperCls, 'neo-face-z');
366
+ break;
367
+ case 2:
368
+ case 3:
369
+ NeoArray.remove(wrapperCls, 'neo-face-x');
370
+ break;
371
+ case 4:
372
+ case 5:
373
+ NeoArray.remove(wrapperCls, 'neo-face-y');
374
+ break;
375
+ }
371
376
 
372
- item.wrapperCls = wrapperCls
377
+ item.wrapperCls = wrapperCls
378
+ }
373
379
  }
374
380
 
375
381
  /**
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
-