neo.mjs 8.0.0-alpha.1 → 8.0.0-beta.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 (66) hide show
  1. package/.github/CODING_GUIDELINES.md +5 -5
  2. package/.github/CONCEPT.md +10 -10
  3. package/apps/ServiceWorker.mjs +2 -2
  4. package/apps/covid/view/MainContainer.mjs +14 -14
  5. package/apps/covid/view/TableContainer.mjs +2 -2
  6. package/apps/portal/index.html +1 -1
  7. package/apps/portal/view/ViewportController.mjs +2 -2
  8. package/apps/portal/view/examples/TabContainer.mjs +11 -11
  9. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  10. package/apps/portal/view/home/parts/MainNeo.mjs +6 -2
  11. package/apps/realworld/view/article/CreateComponent.mjs +1 -1
  12. package/apps/realworld2/view/HomeContainer.mjs +7 -7
  13. package/apps/sharedcovid/view/MainContainer.mjs +17 -17
  14. package/apps/sharedcovid/view/TableContainer.mjs +2 -2
  15. package/buildScripts/createApp.mjs +2 -2
  16. package/buildScripts/createClass.mjs +4 -6
  17. package/docs/app/view/ContentTabContainer.mjs +1 -1
  18. package/docs/app/view/MainContainer.mjs +3 -3
  19. package/docs/app/view/MainContainerController.mjs +9 -9
  20. package/examples/ServiceWorker.mjs +2 -2
  21. package/examples/component/mwc/buttons/MainContainer.mjs +2 -2
  22. package/examples/component/mwc/textFields/MainContainer.mjs +2 -2
  23. package/examples/fields/MainContainer.mjs +197 -228
  24. package/examples/preloadingAssets/view/MainContainer.mjs +2 -2
  25. package/examples/tab/container/MainContainer.mjs +7 -9
  26. package/examples/tabs/MainContainer.mjs +3 -3
  27. package/examples/tabs/MainContainer2.mjs +4 -4
  28. package/examples/videoMove/MainContainer.mjs +14 -7
  29. package/examples/viewport/MainContainer.mjs +3 -3
  30. package/package.json +6 -6
  31. package/resources/data/deck/learnneo/pages/benefits/FormsEngine.md +13 -13
  32. package/resources/data/deck/learnneo/pages/benefits/Speed.md +5 -4
  33. package/resources/data/deck/training/pages/2022-12-27T21-55-23-144Z.md +2 -2
  34. package/resources/data/deck/training/pages/2022-12-28T17-11-34-653Z.md +6 -6
  35. package/resources/data/deck/training/pages/2022-12-29T18-36-08-226Z.md +2 -2
  36. package/resources/data/deck/training/pages/2022-12-29T18-36-56-893Z.md +2 -2
  37. package/resources/data/deck/training/pages/2022-12-29T20-37-08-919Z.md +2 -2
  38. package/resources/data/deck/training/pages/2022-12-29T20-37-20-344Z.md +2 -2
  39. package/resources/data/deck/training/pages/2023-01-01T21-11-58-025Z.md +3 -3
  40. package/resources/data/deck/training/pages/2023-01-16T20-24-09-690Z.md +4 -4
  41. package/resources/data/deck/training/pages/2023-02-05T17-44-53-815Z.md +3 -3
  42. package/resources/data/deck/training/pages/2023-02-05T17-45-40-114Z.md +1 -1
  43. package/resources/scss/src/calendar/view/week/Component.scss +6 -0
  44. package/resources/scss/src/component/DateSelector.scss +1 -0
  45. package/src/DefaultConfig.mjs +2 -2
  46. package/src/calendar/view/MainContainer.mjs +4 -1
  47. package/src/calendar/view/SettingsContainer.mjs +13 -28
  48. package/src/calendar/view/YearComponent.mjs +0 -3
  49. package/src/calendar/view/calendars/ColorsList.mjs +5 -0
  50. package/src/calendar/view/calendars/List.mjs +8 -0
  51. package/src/code/LivePreview.mjs +10 -10
  52. package/src/component/Base.mjs +88 -55
  53. package/src/container/Base.mjs +2 -2
  54. package/src/form/field/Text.mjs +1 -1
  55. package/src/form/field/Time.mjs +5 -3
  56. package/src/form/field/trigger/Time.mjs +2 -1
  57. package/src/layout/Card.mjs +10 -6
  58. package/src/layout/Cube.mjs +15 -4
  59. package/src/list/Base.mjs +7 -1
  60. package/src/list/Color.mjs +3 -3
  61. package/src/main/DomEvents.mjs +1 -1
  62. package/src/main/DomUtils.mjs +39 -36
  63. package/src/tab/Container.mjs +3 -5
  64. package/src/toolbar/Base.mjs +26 -8
  65. package/src/util/VNode.mjs +6 -8
  66. package/src/vdom/Helper.mjs +19 -6
@@ -208,10 +208,10 @@ class Base extends CoreBase {
208
208
  isLoading_: false,
209
209
  /**
210
210
  * Internal flag which will get set to true while an update request (worker messages) is in progress
211
- * @member {Boolean} isVdomUpdating=false
211
+ * @member {Boolean} isVdomUpdating_=false
212
212
  * @protected
213
213
  */
214
- isVdomUpdating: false,
214
+ isVdomUpdating_: false,
215
215
  /**
216
216
  * Using the keys config will create an instance of Neo.util.KeyNavigation.
217
217
  * @see {@link Neo.util.KeyNavigation KeyNavigation}
@@ -404,6 +404,11 @@ class Base extends CoreBase {
404
404
  * @member {String[]} childUpdateCache=[]
405
405
  */
406
406
  childUpdateCache = []
407
+ /**
408
+ * Stores the updateDepth while an update is running to enable checks for parent update collisions
409
+ * @member {Number|null} currentUpdateDepth=null
410
+ */
411
+ currentUpdateDepth = null
407
412
  /**
408
413
  * @member {Function[]} resolveUpdateCache=[]
409
414
  */
@@ -732,13 +737,13 @@ class Base extends CoreBase {
732
737
  * @protected
733
738
  */
734
739
  afterSetIsLoading(value, oldValue) {
735
- if (!(value === false && oldValue === undefined)) {
740
+ if (value || oldValue !== undefined) {
736
741
  let me = this,
737
742
  {cls, vdom} = me,
738
743
  maskIndex;
739
744
 
740
745
  if (oldValue !== undefined && vdom.cn) {
741
- maskIndex = vdom.cn.findIndex(c => c.cls.includes('neo-load-mask'));
746
+ maskIndex = vdom.cn.findLastIndex(c => c.cls?.includes('neo-load-mask'));
742
747
 
743
748
  // Remove the load mask
744
749
  if (maskIndex !== -1) {
@@ -747,19 +752,11 @@ class Base extends CoreBase {
747
752
  }
748
753
 
749
754
  if (value) {
750
- vdom.cn.push(me.loadMask = {
751
- cls: ['neo-load-mask'],
752
- cn : [{
753
- cls: ['neo-load-mask-body'],
754
- cn : [{
755
- cls: me.loadingSpinnerCls
756
- }, {
757
- cls : ['neo-loading-message'],
758
- html : value,
759
- removeDom: !Neo.isString(value)
760
- }]
761
- }]
762
- })
755
+ if (!vdom.cn) {
756
+ vdom.cn = []
757
+ }
758
+
759
+ vdom.cn.push(me.createLoadingMask(value))
763
760
  }
764
761
 
765
762
  NeoArray.toggle(cls, 'neo-masked', value);
@@ -767,6 +764,16 @@ class Base extends CoreBase {
767
764
  }
768
765
  }
769
766
 
767
+ /**
768
+ * Triggered after the isVdomUpdating config got changed
769
+ * @param {Number|null} value
770
+ * @param {Number|null} oldValue
771
+ * @protected
772
+ */
773
+ afterSetIsVdomUpdating(value, oldValue) {
774
+ this.currentUpdateDepth = value ? this.updateDepth : null
775
+ }
776
+
770
777
  /**
771
778
  * Triggered after the maxHeight config got changed
772
779
  * @param {Number|String|null} value
@@ -1388,6 +1395,28 @@ class Base extends CoreBase {
1388
1395
  me.update()
1389
1396
  }
1390
1397
 
1398
+ /**
1399
+ * Override this method in case you need different mask markups.
1400
+ * The removal logic relies on the top level node having the cls 'neo-load-mask'
1401
+ * @param {Boolean|String} loadingMessage
1402
+ * @returns {Object} vdom
1403
+ */
1404
+ createLoadingMask(loadingMessage) {
1405
+ return {
1406
+ cls: ['neo-load-mask'],
1407
+ cn : [{
1408
+ cls: ['neo-load-mask-body'],
1409
+ cn : [{
1410
+ cls: this.loadingSpinnerCls
1411
+ }, {
1412
+ cls : ['neo-loading-message'],
1413
+ html : loadingMessage,
1414
+ removeDom: !Neo.isString(loadingMessage)
1415
+ }]
1416
+ }]
1417
+ }
1418
+ }
1419
+
1391
1420
  /**
1392
1421
  * Creates the tooltip instances
1393
1422
  * @param {Object|String} value
@@ -1830,6 +1859,16 @@ class Base extends CoreBase {
1830
1859
  return this.vnode
1831
1860
  }
1832
1861
 
1862
+ /**
1863
+ * Checks if a given updateDepth & distance would result in an update collision
1864
+ * @param {Number} updateDepth
1865
+ * @param {Number} distance
1866
+ * @returns {Boolean}
1867
+ */
1868
+ hasUpdateCollision(updateDepth, distance) {
1869
+ return updateDepth === -1 ? true : distance < updateDepth
1870
+ }
1871
+
1833
1872
  /**
1834
1873
  * Hide the component.
1835
1874
  * hideMode: 'removeDom' uses vdom removeDom.
@@ -1844,6 +1883,7 @@ class Base extends CoreBase {
1844
1883
  let removeFn = function () {
1845
1884
  if (me.parentId !== 'document.body') {
1846
1885
  me.vdom.removeDom = true;
1886
+ me.parent.updateDepth = 2;
1847
1887
  me.parent.update()
1848
1888
  } else {
1849
1889
  me.unmount()
@@ -1889,29 +1929,35 @@ class Base extends CoreBase {
1889
1929
  * Checks for vdom updates inside the parent chain and if found.
1890
1930
  * Registers the component for a vdom update once done.
1891
1931
  * @param {String} parentId=this.parentId
1892
- * @param {Function} [resolve] gets passed by updateVdom()
1932
+ * @param {Function} [resolve] Gets passed by updateVdom()
1933
+ * @param {Number} distance=1 Distance inside the component tree
1893
1934
  * @returns {Boolean}
1894
1935
  */
1895
- isParentVdomUpdating(parentId=this.parentId, resolve) {
1936
+ isParentUpdating(parentId=this.parentId, resolve, distance=1) {
1896
1937
  if (parentId !== 'document.body') {
1897
1938
  let me = this,
1898
1939
  parent = Neo.getComponent(parentId);
1899
1940
 
1900
1941
  if (parent) {
1901
1942
  if (parent.isVdomUpdating) {
1902
- if (Neo.config.logVdomUpdateCollisions) {
1903
- console.warn('vdom parent update conflict with:', parent, 'for:', me)
1904
- }
1943
+ if (me.hasUpdateCollision(parent.currentUpdateDepth, distance)) {
1944
+ if (Neo.config.logVdomUpdateCollisions) {
1945
+ console.warn('vdom parent update conflict with:', parent, 'for:', me)
1946
+ }
1905
1947
 
1906
- NeoArray.add(parent.childUpdateCache, me.id);
1948
+ NeoArray.add(parent.childUpdateCache, me.id);
1907
1949
 
1908
- // Adding the resolve fn to its own cache, since the parent will trigger
1909
- // a new update() directly on this cmp
1910
- resolve && me.resolveUpdateCache.push(resolve)
1911
- return true
1912
- } else {
1913
- return me.isParentVdomUpdating(parent.parentId, resolve)
1950
+ // Adding the resolve fn to its own cache, since the parent will trigger
1951
+ // a new update() directly on this cmp
1952
+ resolve && me.resolveUpdateCache.push(resolve);
1953
+ return true
1954
+ }
1955
+
1956
+ // If an update is running and does not have a collision, we do not need to check further parents
1957
+ return false
1914
1958
  }
1959
+
1960
+ return me.isParentUpdating(parent.parentId, resolve, distance+1)
1915
1961
  }
1916
1962
  }
1917
1963
 
@@ -2022,22 +2068,24 @@ class Base extends CoreBase {
2022
2068
  * Checks the needsVdomUpdate config inside the parent tree
2023
2069
  * @param {String} parentId=this.parentId
2024
2070
  * @param {Function} [resolve] gets passed by updateVdom()
2071
+ * @param {Number} distance=1 Distance inside the component tree
2025
2072
  * @returns {Boolean}
2026
2073
  */
2027
- needsParentUpdate(parentId=this.parentId, resolve) {
2074
+ needsParentUpdate(parentId=this.parentId, resolve, distance=1) {
2028
2075
  if (parentId !== 'document.body') {
2029
2076
  let me = this,
2030
2077
  parent = Neo.getComponent(parentId);
2031
2078
 
2032
2079
  if (parent) {
2033
- if (parent.needsVdomUpdate) {
2080
+ // We are checking for parent.updateDepth, since we care about the depth of the next update cycle
2081
+ if (parent.needsVdomUpdate && me.hasUpdateCollision(parent.updateDepth, distance)) {
2034
2082
  parent.resolveUpdateCache.push(...me.resolveUpdateCache);
2035
2083
  resolve && parent.resolveUpdateCache.push(resolve);
2036
2084
  me.resolveUpdateCache = [];
2037
2085
  return true
2038
- } else {
2039
- return me.needsParentUpdate(parent.parentId)
2040
2086
  }
2087
+
2088
+ return me.needsParentUpdate(parent.parentId, resolve, distance+1)
2041
2089
  }
2042
2090
  }
2043
2091
 
@@ -2148,7 +2196,7 @@ class Base extends CoreBase {
2148
2196
  * @param {Neo.vdom.VNode} vnode= this.vnode
2149
2197
  * @returns {Promise<any>}
2150
2198
  */
2151
- promiseUpdate(vdom = this.vdom, vnode = this.vnode) {
2199
+ promiseUpdate(vdom=this.vdom, vnode=this.vnode) {
2152
2200
  return new Promise((resolve, reject) => {
2153
2201
  this.updateVdom(vdom, vnode, resolve, reject)
2154
2202
  })
@@ -2349,6 +2397,7 @@ class Base extends CoreBase {
2349
2397
  if (me.silentVdomUpdate) {
2350
2398
  me.needsVdomUpdate = true
2351
2399
  } else if (me.parentId !== 'document.body') {
2400
+ me.parent.updateDepth = 2;
2352
2401
  me.parent.update()
2353
2402
  } else {
2354
2403
  !me.mounted && me.render(true)
@@ -2398,7 +2447,7 @@ class Base extends CoreBase {
2398
2447
 
2399
2448
  // we need one separate iteration first to ensure all wrapper nodes get registered
2400
2449
  childComponents.forEach(component => {
2401
- childVnode = VNodeUtil.findChildVnode(me.vnode, component.vdom.id)?.vnode;
2450
+ childVnode = VNodeUtil.find(me.vnode, component.vdom.id)?.vnode;
2402
2451
 
2403
2452
  if (childVnode) {
2404
2453
  map[component.id] = childVnode;
@@ -2486,7 +2535,7 @@ class Base extends CoreBase {
2486
2535
  updateCls(cls, oldCls, id=this.id) {
2487
2536
  let me = this,
2488
2537
  {vnode} = me,
2489
- vnodeTarget = vnode && VNodeUtil.findChildVnode(me.vnode, {id})?.vnode;
2538
+ vnodeTarget = vnode && VNodeUtil.find(me.vnode, {id})?.vnode;
2490
2539
 
2491
2540
  if (vnode && !Neo.isEqual(cls, oldCls)) {
2492
2541
  if (vnodeTarget) {
@@ -2518,7 +2567,7 @@ class Base extends CoreBase {
2518
2567
 
2519
2568
  if (delta) {
2520
2569
  vdom = VDomUtil.find(me.vdom, id);
2521
- vnode = me.vnode && VNodeUtil.findChildVnode(me.vnode, id);
2570
+ vnode = me.vnode && VNodeUtil.find(me.vnode, id);
2522
2571
 
2523
2572
  if (!me.hasUnmountedVdomChanges) {
2524
2573
  me.hasUnmountedVdomChanges = !me.mounted && me.hasBeenMounted
@@ -2569,24 +2618,8 @@ class Base extends CoreBase {
2569
2618
  {app, mounted, parentId} = me,
2570
2619
  listenerId;
2571
2620
 
2572
- // It is important to keep the vdom tree stable to ensure that containers do not lose the references to their
2573
- // child vdom trees. The if case should not happen, but in case it does, keeping the reference and merging
2574
- // the content over seems to be the best strategy
2575
- if (me._vdom !== vdom) {
2576
- Logger.warn('vdom got replaced for: ' + me.id + '. Copying the content into the reference holder object');
2577
-
2578
- Object.keys(me._vdom).forEach(key => {
2579
- delete me._vdom[key]
2580
- });
2581
-
2582
- vdom = Object.assign(me._vdom, vdom)
2583
- }
2584
-
2585
- if (resolve && me.isVdomUpdating) {
2586
- me.resolveUpdateCache.push(resolve)
2587
- }
2588
-
2589
2621
  if (me.isVdomUpdating || me.silentVdomUpdate) {
2622
+ resolve && me.resolveUpdateCache.push(resolve);
2590
2623
  me.needsVdomUpdate = true
2591
2624
  } else {
2592
2625
  if (!mounted && me.isConstructed && !me.hasRenderingListener && app?.rendering === true) {
@@ -2606,7 +2639,7 @@ class Base extends CoreBase {
2606
2639
 
2607
2640
  if (
2608
2641
  !me.needsParentUpdate(parentId, resolve)
2609
- && !me.isParentVdomUpdating(parentId, resolve)
2642
+ && !me.isParentUpdating(parentId, resolve)
2610
2643
  && mounted
2611
2644
  && vnode
2612
2645
  ) {
@@ -168,7 +168,7 @@ class Base extends Component {
168
168
  if (oldValue !== undefined) {
169
169
  super.afterSetMounted(value, oldValue);
170
170
 
171
- for (let i = 0, { items } = this, { length } = items; i < length; i++) {
171
+ for (let i = 0, {items} = this, {length} = items; i < length; i++) {
172
172
  if (!items[i].vdom.removeDom) {
173
173
  items[i].mounted = value
174
174
  }
@@ -500,7 +500,7 @@ class Base extends Component {
500
500
  /**
501
501
  * Inserts an item or array of items at a specific index
502
502
  * @param {Number} index
503
- * @param {Object|Array} item
503
+ * @param {Array|Object} item
504
504
  * @param {Boolean} [silent=false]
505
505
  * @returns {Neo.component.Base|Neo.component.Base[]}
506
506
  */
@@ -1356,7 +1356,7 @@ class Text extends Base {
1356
1356
  let me = this,
1357
1357
  oldValue = me.value,
1358
1358
  inputValue = data.value,
1359
- vnode = VNodeUtil.findChildVnode(me.vnode, {nodeName: 'input'});
1359
+ vnode = VNodeUtil.find(me.vnode, {nodeName: 'input'});
1360
1360
 
1361
1361
  if (vnode) {
1362
1362
  // Update the current state (modified DOM by the user) to enable the delta-updates logic.
@@ -101,9 +101,11 @@ class Time extends Picker {
101
101
  });
102
102
 
103
103
  me.list = Neo.create({
104
- module : List,
105
- displayField: 'value',
106
- store : me.collection,
104
+ module : List,
105
+ displayField : 'value',
106
+ itemsFocusable: false,
107
+ navigator : {eventSource: me.getInputElId()},
108
+ store : me.collection,
107
109
  ...me.listConfig
108
110
  });
109
111
 
@@ -62,10 +62,11 @@ class Time extends Picker {
62
62
  parentId: me.id,
63
63
  showDisc: false,
64
64
  size : null,
65
+ windowId: me.windowId,
65
66
  ...me.clockConfig
66
67
  });
67
68
 
68
- me.vdom.cn = [me.clock.vdom];
69
+ me.vdom.cn = [me.clock.createVdomReference()];
69
70
  me.update()
70
71
  }
71
72
 
@@ -201,14 +201,14 @@ class Card extends Base {
201
201
  item.className = proto.className;
202
202
  item.module = module;
203
203
 
204
- me.applyChildAttributes(item, index);
205
-
206
204
  delete item.isLoading;
207
205
  delete item.vdom;
208
206
 
209
207
  items[index] = item = Neo.create(item);
210
208
 
211
- container.getVdomItemsRoot().cn[index] = item.vdom;
209
+ me.applyChildAttributes(item, index);
210
+
211
+ container.getVdomItemsRoot().cn[index] = item.createVdomReference();
212
212
 
213
213
  container.fire('cardLoaded', {item});
214
214
 
@@ -253,13 +253,15 @@ class Card extends Base {
253
253
 
254
254
  vdom.cn = [
255
255
  {cls: ['neo-relative'], cn: [
256
- {cls: ['neo-animation-wrapper'], style, cn: [card.vdom]}
256
+ {cls: ['neo-animation-wrapper'], style, cn: [card.createVdomReference()]}
257
257
  ]}
258
258
  ];
259
259
 
260
260
  animationWrapper = vdom.cn[0].cn[0];
261
261
 
262
- animationWrapper.cn[slideIn ? 'unshift' : 'push'](oldCard.vdom);
262
+ animationWrapper.cn[slideIn ? 'unshift' : 'push'](oldCard.createVdomReference());
263
+
264
+ container.updateDepth = -1;
263
265
 
264
266
  await container.promiseUpdate();
265
267
 
@@ -274,11 +276,13 @@ class Card extends Base {
274
276
  vdom.cn = [];
275
277
 
276
278
  container.items.forEach(item => {
277
- vdom.cn.push(item.vdom)
279
+ vdom.cn.push(item.createVdomReference())
278
280
  });
279
281
 
280
282
  oldCard.vdom.removeDom = true;
281
283
 
284
+ container.updateDepth = -1;
285
+
282
286
  await container.promiseUpdate()
283
287
  }
284
288
  }
@@ -144,6 +144,7 @@ class Cube extends Card {
144
144
 
145
145
  if (Neo.typeOf(item.module) === 'Function') {
146
146
  await me.loadModule(item, value);
147
+ container.updateDepth = -1;
147
148
  container.update();
148
149
 
149
150
  await me.timeout(100) // wait for the view to get painted first
@@ -276,6 +277,14 @@ class Cube extends Card {
276
277
  }
277
278
  }
278
279
 
280
+ /**
281
+ * @protected
282
+ */
283
+ applyRenderAttributes() {
284
+ this.container.updateDepth = -1;
285
+ super.applyRenderAttributes()
286
+ }
287
+
279
288
  /**
280
289
  *
281
290
  */
@@ -299,9 +308,9 @@ class Cube extends Card {
299
308
  vdom.cn = container.getVdomItemsRoot().cn;
300
309
 
301
310
  if (me.hideInactiveCardsOnDestroy) {
302
- vdom.cn.forEach((item, index) => {
311
+ container.items.forEach((item, index) => {
303
312
  if (index < 6 && index !== me.activeIndex) {
304
- item.removeDom = true
313
+ item.vdom.removeDom = true
305
314
  }
306
315
  })
307
316
  }
@@ -309,6 +318,7 @@ class Cube extends Card {
309
318
  // override
310
319
  container.getVdomItemsRoot = me.#cachedVdomItemsRoot;
311
320
 
321
+ container.updateDepth = -1;
312
322
  container.update();
313
323
 
314
324
  super.destroy(...args)
@@ -336,12 +346,13 @@ class Cube extends Card {
336
346
 
337
347
  me.timeout(50).then(() => {
338
348
  // Important when switching from a card layout to this one
339
- container.vdom.cn[0].cn[0].cn.forEach((node, index) => {
349
+ container.items.forEach((item, index) => {
340
350
  if (index < 6) {
341
- delete node.removeDom
351
+ delete item.vdom.removeDom
342
352
  }
343
353
  });
344
354
 
355
+ container.updateDepth = -1;
345
356
  container.update()
346
357
  })
347
358
  }
package/src/list/Base.mjs CHANGED
@@ -125,7 +125,12 @@ class Base extends Component {
125
125
  */
126
126
  selectionModel_: null,
127
127
  /**
128
- * Set this to true in case a select event should only update _vdom (e.g. when used inside a form.field.Select
128
+ * Set this to true in case a keyboard navigation should immediately select the focussed item
129
+ * @member {Boolean} selectOnFocus=false
130
+ */
131
+ selectOnFocus: false,
132
+ /**
133
+ * Set this to true in case a select event should only update _vdom (e.g. when used inside a form.field.ComboBox
129
134
  * @member {Boolean} silentSelect=false
130
135
  */
131
136
  silentSelect: false,
@@ -298,6 +303,7 @@ class Base extends Component {
298
303
  if (!me.hasNavigator) {
299
304
  me.navigator = {
300
305
  appName : me.appName,
306
+ autoClick : me.selectOnFocus,
301
307
  id : me.id,
302
308
  keepFocusIndex: me.keepFocusIndex,
303
309
  selector : `.${me.itemCls}:not(.neo-disabled,.neo-list-header)`,
@@ -22,13 +22,13 @@ class Color extends Base {
22
22
  baseCls: ['neo-color-list', 'neo-list'],
23
23
  /**
24
24
  * The data.Model field which contains the color value
25
- * @member {String} colorField='name'
25
+ * @member {String} colorField_='name'
26
26
  */
27
- colorField: 'name',
27
+ colorField_: 'name',
28
28
  /**
29
29
  * Override the formatter to apply a custom background-color styling.
30
30
  * E.g. using CSS vars for different themes
31
- * @member {Function} colorField=(scope,data)=>data[scope.colorField]
31
+ * @member {Function} colorFormatter=(scope,data)=>data[scope.colorField]
32
32
  */
33
33
  colorFormatter: (scope,data) => data[scope.colorField]
34
34
  }
@@ -98,7 +98,7 @@ class DomEvents extends Base {
98
98
  singleton: true,
99
99
  /**
100
100
  * Remote method access for other workers
101
- * @member {Object} remote={app: ['addDomListener']}
101
+ * @member {Object} remote
102
102
  * @protected
103
103
  */
104
104
  remote: {
@@ -1,22 +1,22 @@
1
1
  import Base from '../core/Base.mjs';
2
2
 
3
3
  const focusableTags = {
4
- BODY : 1,
5
- BUTTON : 1,
6
- EMBED : 1,
7
- IFRAME : 1,
8
- INPUT : 1,
9
- OBJECT : 1,
10
- SELECT : 1,
11
- TEXTAREA : 1
4
+ BODY : 1,
5
+ BUTTON : 1,
6
+ EMBED : 1,
7
+ IFRAME : 1,
8
+ INPUT : 1,
9
+ OBJECT : 1,
10
+ SELECT : 1,
11
+ TEXTAREA: 1
12
12
  };
13
13
 
14
14
  /**
15
- * @class Neo.main.DomUtils
15
+ * @class Neo.main.DomUtils
16
16
  * @extends Neo.core.Base
17
17
  * @singleton
18
18
  */
19
- export default class DomUtils extends Base {
19
+ class DomUtils extends Base {
20
20
  static config = {
21
21
  /**
22
22
  * @member {String} className='Neo.main.DomUtils'
@@ -31,42 +31,21 @@ export default class DomUtils extends Base {
31
31
  * @param {HTMLElement} el The element to start from.
32
32
  * @param {Function} filterFn A function which returns `true` when the desired element is reached.
33
33
  * @param {HTMLElement} [limit] The element to stop at. This is *not* considered for matching.
34
- * @returns {Boolean}
34
+ * @returns {HTMLElement}
35
35
  */
36
36
  static closest(el, filterFn, limit = document.body) {
37
37
  while (el?.nodeType === Node.ELEMENT_NODE && el !== limit) {
38
38
  if (filterFn(el)) {
39
- return el;
39
+ return el
40
40
  }
41
- el = el.parentNode;
42
- }
43
- }
44
-
45
- /**
46
- * Analogous to the `HTMLElement` `querySelectorAll` method. Searches the passed element
47
- * and all descendants for all elements for which the passed `filterFn` returns `true`.
48
- * @param {HTMLElement} el The element to start from.
49
- * @param {Function} filterFn A function which returns `true` when a desired element is reached.
50
- * @returns {HTMLElement[]} An array of matching elements
51
- */
52
- static queryAll(el, filterFn) {
53
- return [el, ...el.querySelectorAll('*')].filter(filterFn);
54
- }
55
41
 
56
- /**
57
- * Analogous to the `HTMLElement` `querySelector` method. Searches the passed element
58
- * and all descendants for the first element for which the passed `filterFn` returns `true`.
59
- * @param {HTMLElement} el The element to start from.
60
- * @param {Function} filterFn A function which returns `true` when the desired element is reached.
61
- * @returns {HTMLElement} The first matching element
62
- */
63
- static query(el, filterFn) {
64
- return [el, ...el.querySelectorAll('*')].find(filterFn);
42
+ el = el.parentNode
43
+ }
65
44
  }
66
45
 
67
46
  static isFocusable(e) {
68
47
  // May be used as a scopeless callback, so use "DomUtils", not "this"
69
- return DomUtils.isTabbable(e) || Number(e.getAttribute('tabIndex')) < 0;
48
+ return DomUtils.isTabbable(e) || Number(e.getAttribute('tabIndex')) < 0
70
49
  }
71
50
 
72
51
  static isTabbable(e) {
@@ -86,4 +65,28 @@ export default class DomUtils extends Base {
86
65
  (tabIndex != null && Number(tabIndex) >= 0) ||
87
66
  e.contentEditable === 'true'
88
67
  }
68
+
69
+ /**
70
+ * Analogous to the `HTMLElement` `querySelector` method. Searches the passed element
71
+ * and all descendants for the first element for which the passed `filterFn` returns `true`.
72
+ * @param {HTMLElement} el The element to start from.
73
+ * @param {Function} filterFn A function which returns `true` when the desired element is reached.
74
+ * @returns {HTMLElement} The first matching element
75
+ */
76
+ static query(el, filterFn) {
77
+ return [el, ...el.querySelectorAll('*')].find(filterFn);
78
+ }
79
+
80
+ /**
81
+ * Analogous to the `HTMLElement` `querySelectorAll` method. Searches the passed element
82
+ * and all descendants for all elements for which the passed `filterFn` returns `true`.
83
+ * @param {HTMLElement} el The element to start from.
84
+ * @param {Function} filterFn A function which returns `true` when a desired element is reached.
85
+ * @returns {HTMLElement[]} An array of matching elements
86
+ */
87
+ static queryAll(el, filterFn) {
88
+ return [el, ...el.querySelectorAll('*')].filter(filterFn)
89
+ }
89
90
  }
91
+
92
+ export default Neo.setupClass(DomUtils);
@@ -243,7 +243,7 @@ class Container extends BaseContainer {
243
243
  });
244
244
 
245
245
  items.forEach((item, index) => {
246
- tabButtons.push(me.getTabButtonConfig(item.tabButtonConfig, index));
246
+ tabButtons.push(me.getTabButtonConfig(item.header, index));
247
247
 
248
248
  if (!(item instanceof Neo.component.Base)) {
249
249
  item = {flex: 1, ...me.itemDefaults, isTab: true, ...item}
@@ -410,7 +410,7 @@ class Container extends BaseContainer {
410
410
  cardContainer = me.getCardContainer(),
411
411
  tabBar = me.getTabBar(),
412
412
  hasItem = false,
413
- i, len, returnArray, superItem, tab, tabButtonConfig;
413
+ i, len, returnArray, superItem, tab;
414
414
 
415
415
  if (Array.isArray(item)) {
416
416
  i = 0;
@@ -441,9 +441,7 @@ class Container extends BaseContainer {
441
441
  }
442
442
 
443
443
  if (!hasItem) {
444
- tabButtonConfig = item.tabButtonConfig;
445
-
446
- tab = tabBar.insert(index, me.getTabButtonConfig(tabButtonConfig, index));
444
+ tab = tabBar.insert(index, me.getTabButtonConfig(item.header, index));
447
445
 
448
446
  // todo: non index based matching of tab buttons and cards
449
447
  i = 0;