neo.mjs 8.43.0 → 9.0.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 (47) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/colors/view/ViewportController.mjs +2 -6
  3. package/apps/portal/index.html +1 -1
  4. package/apps/portal/view/ViewportController.mjs +3 -3
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  6. package/apps/portal/view/learn/ContentComponent.mjs +5 -5
  7. package/apps/portal/view/learn/MainContainerController.mjs +0 -23
  8. package/apps/sharedcovid/view/MainContainerController.mjs +4 -13
  9. package/examples/ServiceWorker.mjs +2 -2
  10. package/examples/button/base/MainContainer.mjs +9 -1
  11. package/examples/component/multiWindowCoronaGallery/ViewportController.mjs +4 -11
  12. package/examples/component/multiWindowHelix/ViewportController.mjs +1 -7
  13. package/package.json +5 -5
  14. package/src/DefaultConfig.mjs +2 -2
  15. package/src/button/Base.mjs +28 -7
  16. package/src/component/Base.mjs +43 -86
  17. package/src/component/wrapper/AmChart.mjs +4 -3
  18. package/src/container/Base.mjs +15 -3
  19. package/src/controller/Component.mjs +4 -106
  20. package/src/core/Base.mjs +15 -2
  21. package/src/core/Observable.mjs +9 -11
  22. package/src/data/Model.mjs +9 -0
  23. package/src/data/RecordFactory.mjs +1 -1
  24. package/src/form/field/Text.mjs +3 -1
  25. package/src/grid/Container.mjs +2 -4
  26. package/src/grid/VerticalScrollbar.mjs +3 -2
  27. package/src/grid/View.mjs +37 -45
  28. package/src/grid/column/AnimatedChange.mjs +1 -1
  29. package/src/grid/column/Component.mjs +14 -5
  30. package/src/grid/header/Button.mjs +6 -17
  31. package/src/main/DomEvents.mjs +26 -1
  32. package/src/manager/DomEvent.mjs +21 -22
  33. package/src/selection/Model.mjs +14 -11
  34. package/src/selection/grid/BaseModel.mjs +155 -2
  35. package/src/selection/grid/CellColumnModel.mjs +7 -11
  36. package/src/selection/grid/CellColumnRowModel.mjs +7 -11
  37. package/src/selection/grid/CellModel.mjs +5 -4
  38. package/src/selection/grid/CellRowModel.mjs +22 -30
  39. package/src/selection/grid/ColumnModel.mjs +7 -11
  40. package/src/selection/grid/RowModel.mjs +21 -35
  41. package/src/state/Provider.mjs +3 -8
  42. package/src/tab/Container.mjs +17 -1
  43. package/src/table/Container.mjs +34 -68
  44. package/src/table/View.mjs +51 -3
  45. package/src/table/header/Button.mjs +7 -18
  46. package/src/util/Function.mjs +3 -1
  47. package/src/util/VDom.mjs +4 -4
@@ -1,6 +1,5 @@
1
1
  import Base from './Base.mjs';
2
2
  import {resolveCallback} from '../util/Function.mjs';
3
- import Logger from '../util/Logger.mjs';
4
3
 
5
4
  /**
6
5
  * @class Neo.controller.Component
@@ -70,17 +69,18 @@ class Component extends Base {
70
69
 
71
70
  /**
72
71
  * @param {String} handlerName
73
- * @param {Neo.component.Base} [component]
72
+ * @param {Neo.component.Base|null} [component]
74
73
  * @returns {Neo.controller.Component|Boolean|null}
75
74
  */
76
75
  getHandlerScope(handlerName, component) {
77
76
  let me = this,
78
- {parent} = me;
77
+ {parent} = me,
78
+ handlerCb;
79
79
 
80
80
  if (component) {
81
81
  // Look for ths function *name* first in the Component itself.
82
82
  // If we find it, return true so calling code knows not to continue to search.
83
- const handlerCb = resolveCallback(handlerName, component);
83
+ handlerCb = resolveCallback(handlerName, component);
84
84
 
85
85
  // Handler fn is resolved in the Component or its own parent chain.
86
86
  // Return a status indicating that we do not need an early binding
@@ -162,108 +162,6 @@ class Component extends Base {
162
162
  */
163
163
  onComponentConstructed() {}
164
164
 
165
- /**
166
- * @param {Neo.component.Base} component=this.component
167
- */
168
- parseConfig(component=this.component) {
169
- let me = this,
170
- {handler, listeners, reference, renderer, validator} = component,
171
- eventHandler, handlerScope;
172
-
173
- if (handler && Neo.isString(handler)) {
174
- handlerScope = me.getHandlerScope(handler, component);
175
-
176
- // If the handler name was not resolved in the Component itself, bind it
177
- if (handlerScope !== true) {
178
- component.handler = handlerScope[handler].bind(component.handlerScope || handlerScope);
179
- }
180
- }
181
-
182
- listeners && Object.entries(listeners).forEach(([key, value]) => {
183
- if (key !== 'scope' && key !== 'delegate') {
184
- if (Neo.isString(value)) {
185
- eventHandler = value;
186
- handlerScope = me.getHandlerScope(eventHandler, component);
187
-
188
- if (!handlerScope) {
189
- Logger.logError('Unknown event handler for', eventHandler, component)
190
- } else if (handlerScope !== true) {
191
- listeners[key] = {};
192
- listeners[key].fn = handlerScope[eventHandler].bind(handlerScope)
193
- }
194
- } else {
195
- value?.forEach?.(listener => {
196
- if (Neo.isObject(listener) && listener.hasOwnProperty('fn') && Neo.isString(listener.fn)) {
197
- eventHandler = listener.fn;
198
- handlerScope = me.getHandlerScope(eventHandler, component);
199
-
200
- if (!handlerScope) {
201
- console.error('Unknown event handler for', eventHandler, component)
202
- } else if (handlerScope !== true) {
203
- listener.fn = handlerScope[eventHandler].bind(handlerScope)
204
- }
205
- }
206
- })
207
- }
208
- }
209
- });
210
-
211
- if (renderer && Neo.isString(renderer)) {
212
- handlerScope = me.getHandlerScope(renderer);
213
-
214
- if (handlerScope) {
215
- component.renderer = handlerScope[renderer].bind(handlerScope)
216
- }
217
- }
218
-
219
- if (validator && Neo.isString(validator)) {
220
- handlerScope = me.getHandlerScope(validator);
221
-
222
- if (!handlerScope) {
223
- Logger.logError('Unknown validator for', component.id, component)
224
- } else {
225
- component.validator = handlerScope[validator].bind(handlerScope)
226
- }
227
- }
228
-
229
- if (reference) {
230
- me.references[reference] = component
231
- }
232
- }
233
-
234
- /**
235
- * @param {Neo.component.Base} component=this.component
236
- */
237
- parseDomListeners(component=this.component) {
238
- let me = this,
239
- {domListeners} = component,
240
- eventHandler, scope;
241
-
242
- domListeners?.forEach(domListener => {
243
- Object.entries(domListener).forEach(([key, value]) => {
244
- eventHandler = null;
245
-
246
- if (key !== 'scope' && key !== 'delegate') {
247
- if (Neo.isString(value)) {
248
- eventHandler = value;
249
- } else if (Neo.isObject(value) && value.hasOwnProperty('fn') && Neo.isString(value.fn)) {
250
- eventHandler = value.fn;
251
- }
252
-
253
- if (eventHandler) {
254
- scope = me.getHandlerScope(eventHandler);
255
-
256
- // There can be string based listeners like 'up.onClick', which will resolved inside manager.DomEvents
257
- // => Do nothing in case there is no match inside the controller hierarchy.
258
- if (scope) {
259
- domListener[key] = scope[eventHandler].bind(scope)
260
- }
261
- }
262
- }
263
- })
264
- })
265
- }
266
-
267
165
  /**
268
166
  * Will get called by component.Base: destroy() in case the component has a reference config
269
167
  * @param {Neo.component.Base} component
package/src/core/Base.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import {buffer, debounce, intercept, throttle} from '../util/Function.mjs';
2
- import IdGenerator from './IdGenerator.mjs'
1
+ import {buffer, debounce, intercept, resolveCallback, throttle} from '../util/Function.mjs';
2
+ import IdGenerator from './IdGenerator.mjs'
3
3
 
4
4
  const configSymbol = Symbol.for('configSymbol'),
5
5
  forceAssignConfigs = Symbol('forceAssignConfigs'),
@@ -255,6 +255,19 @@ class Base {
255
255
  return value
256
256
  }
257
257
 
258
+ /**
259
+ * @param {String} fn The name of a function to find in the passed scope object.
260
+ * @param {Object} originName The name of the method inside the originScope.
261
+ * @param {Object} scope The scope to find the function in if it is specified as a string.
262
+ * @param {Object} originScope=this The scope where the function is located.
263
+ */
264
+ bindCallback(fn, originName, scope=this, originScope=this) {
265
+ if (fn && Neo.isString(fn)) {
266
+ const handler = resolveCallback(fn, scope);
267
+ originScope[originName] = handler.fn.bind(handler.scope)
268
+ }
269
+ }
270
+
258
271
  /**
259
272
  * From within an overwrite, a method can call a parent method, by using callOverwritten.
260
273
  *
@@ -101,14 +101,12 @@ class Observable extends Base {
101
101
  }
102
102
 
103
103
  if (!nameObject) {
104
- eventConfig = {
105
- data,
106
- delay,
107
- fn: listener,
108
- id: eventId || Neo.getId('event'),
109
- once,
110
- scope
111
- };
104
+ eventConfig = {fn: listener, id: eventId || Neo.getId('event')};
105
+
106
+ if (data) {eventConfig.data = data}
107
+ if (delay > 0) {eventConfig.delay = delay}
108
+ if (once) {eventConfig.once = once}
109
+ if (scope) {eventConfig.scope = scope}
112
110
 
113
111
  if (existing = me.listeners?.[name]) {
114
112
  existing.forEach(cfg => {
@@ -137,9 +135,9 @@ class Observable extends Base {
137
135
  /**
138
136
  * Call the passed function, or a function by *name* which exists in the passed scope's
139
137
  * or this component's ownership chain.
140
- * @param {Function|String} fn A function, or the name of a function to find in the passed scope object/
141
- * @param {Object} scope The scope to find the function in if it is specified as a string.
142
- * @param {Array} args Arguments to pass to the callback.
138
+ * @param {Function|String} fn A function, or the name of a function to find in the passed scope object.
139
+ * @param {Object} scope The scope to find the function in if it is specified as a string.
140
+ * @param {Array} args Arguments to pass to the callback.
143
141
  */
144
142
  callback(fn, scope=this, args) {
145
143
  if (fn) {
@@ -86,6 +86,15 @@ class Model extends Base {
86
86
  return this.fieldsMap.get(name) || null
87
87
  }
88
88
 
89
+ /**
90
+ * Finds a field type by a given field name
91
+ * @param {String} name
92
+ * @returns {String|null} The lowercase field type or null if no match was found
93
+ */
94
+ getFieldType(name) {
95
+ return this.getField(name)?.type?.toLowerCase() || null
96
+ }
97
+
89
98
  /**
90
99
  * @param {Object[]} fields
91
100
  * @param {Boolean} isRoot=true
@@ -233,7 +233,7 @@ class RecordFactory extends Base {
233
233
  * @returns {Object}
234
234
  */
235
235
  toJSON() {
236
- return this[dataSymbol]
236
+ return structuredClone(this[dataSymbol])
237
237
  }
238
238
  };
239
239
 
@@ -1600,7 +1600,9 @@ class Text extends Field {
1600
1600
  } else if (inputPattern && !inputPattern.test(value)) {
1601
1601
  me._error = me.errorTextInputPattern(errorParam);
1602
1602
  returnValue = false
1603
- } else if (Neo.isFunction(me.validator)) {
1603
+ } else if (me.validator) {
1604
+ me.bindCallback(me.validator, 'validator');
1605
+
1604
1606
  errorText = me.validator(me);
1605
1607
 
1606
1608
  if (errorText !== true) {
@@ -459,9 +459,7 @@ class GridContainer extends BaseContainer {
459
459
  parent : me,
460
460
  windowId: me.windowId,
461
461
  ...column
462
- });
463
-
464
- me.getController()?.parseConfig(columns[index])
462
+ })
465
463
  });
466
464
 
467
465
  me.items[0].items = headerButtons;
@@ -563,7 +561,7 @@ class GridContainer extends BaseContainer {
563
561
 
564
562
  me.store.sort(opts);
565
563
  me.removeSortingCss(opts.property);
566
- me.view.onStoreLoad()
564
+ opts.direction && me.view.onStoreLoad()
567
565
  }
568
566
 
569
567
  /**
@@ -95,8 +95,9 @@ class VerticalScrollbar extends Component {
95
95
  let me = this;
96
96
 
97
97
  value.on({
98
- load : me.updateScrollHeight,
99
- scope: me
98
+ filter: me.updateScrollHeight,
99
+ load : me.updateScrollHeight,
100
+ scope : me
100
101
  });
101
102
 
102
103
  value.getCount() > 0 && me.updateScrollHeight()
package/src/grid/View.mjs CHANGED
@@ -181,10 +181,6 @@ class GridView extends Component {
181
181
  get selectedRows() {
182
182
  let {selectionModel} = this;
183
183
 
184
- if (selectionModel.ntype === 'selection-grid-rowmodel') {
185
- return selectionModel.items
186
- }
187
-
188
184
  if (selectionModel.ntype?.includes('row')) {
189
185
  return selectionModel.selectedRows
190
186
  }
@@ -450,6 +446,8 @@ class GridView extends Component {
450
446
  column.rendererScope = column;
451
447
  }
452
448
 
449
+ me.bindCallback(column.renderer, 'renderer', column.rendererScope || me, column);
450
+
453
451
  rendererOutput = column.renderer.call(column.rendererScope || me, {
454
452
  column,
455
453
  columnIndex,
@@ -496,7 +494,7 @@ class GridView extends Component {
496
494
  }
497
495
 
498
496
  if (!cellId) {
499
- cellId = me.getCellId(record, column.dataField)
497
+ cellId = me.getCellId(rowIndex, column.dataField)
500
498
  }
501
499
 
502
500
  if (selectedCells.includes(cellId)) {
@@ -575,7 +573,8 @@ class GridView extends Component {
575
573
  {mountedColumns, selectedRows} = me,
576
574
  gridContainer = me.parent,
577
575
  {columns} = gridContainer,
578
- id = me.getRowId(record, rowIndex),
576
+ id = me.getRowId(rowIndex),
577
+ recordId = record[me.store.getKeyProperty()],
579
578
  rowCls = me.getRowClass(record, rowIndex),
580
579
  config, column, columnPosition, gridRow, i;
581
580
 
@@ -584,12 +583,7 @@ class GridView extends Component {
584
583
  }
585
584
 
586
585
  if (selectedRows && record[me.selectedRecordField]) {
587
- NeoArray.add(selectedRows, id)
588
- }
589
-
590
- if (selectedRows?.includes(id)) {
591
- rowCls.push('neo-selected');
592
- gridContainer.fire('select', {record})
586
+ NeoArray.add(selectedRows, recordId)
593
587
  }
594
588
 
595
589
  gridRow = {
@@ -597,6 +591,7 @@ class GridView extends Component {
597
591
  'aria-rowindex': rowIndex + 2, // header row => 1, first body row => 2
598
592
  cls : rowCls,
599
593
  cn : [],
594
+ data : {recordId},
600
595
  role : 'row',
601
596
 
602
597
  style: {
@@ -605,6 +600,12 @@ class GridView extends Component {
605
600
  }
606
601
  };
607
602
 
603
+ if (selectedRows?.includes(recordId)) {
604
+ rowCls.push('neo-selected');
605
+ gridRow['aria-selected'] = true;
606
+ gridContainer.fire('select', {record})
607
+ }
608
+
608
609
  for (i=mountedColumns[0]; i <= mountedColumns[1]; i++) {
609
610
  column = columns.getAt(i);
610
611
  config = me.applyRendererOutput({column, columnIndex: i, record, rowIndex});
@@ -709,12 +710,12 @@ class GridView extends Component {
709
710
  }
710
711
 
711
712
  /**
712
- * @param {Object} record
713
+ * @param {Number} rowIndex
713
714
  * @param {String} dataField
714
715
  * @returns {String}
715
716
  */
716
- getCellId(record, dataField) {
717
- return this.id + '__' + record[this.store.getKeyProperty()] + '__' + dataField
717
+ getCellId(rowIndex, dataField) {
718
+ return this.getRowId(rowIndex) + '__' + dataField
718
719
  }
719
720
 
720
721
  /**
@@ -803,36 +804,23 @@ class GridView extends Component {
803
804
  return null
804
805
  }
805
806
 
806
- /**
807
- * @param {String} cellId
808
- * @returns {Record}
809
- */
810
- getRecordByCellId(cellId) {
811
- let recordId = cellId.split('__')[1],
812
- {store} = this,
813
- keyType = store.getKeyType();
814
-
815
- if (keyType === 'int' || keyType === 'integer') {
816
- recordId = parseInt(recordId)
817
- }
818
-
819
- return store.get(recordId)
820
- }
821
-
822
807
  /**
823
808
  * @param {String} rowId
824
- * @returns {Record}
809
+ * @returns {Record|null}
825
810
  */
826
811
  getRecordByRowId(rowId) {
827
- let recordId = rowId.split('__')[2],
828
- {store} = this,
829
- keyType = store.getKeyType();
812
+ let me = this,
813
+ node = me.getVdomChild(rowId),
814
+ rowIndex = node['aria-rowindex'];
815
+
816
+ if (Neo.isNumber(rowIndex)) {
817
+ // aria-rowindex is 1 based & also includes the header
818
+ rowIndex -= 2;
830
819
 
831
- if (keyType === 'int' || keyType === 'integer') {
832
- recordId = parseInt(recordId)
820
+ return me.store.getAt(rowIndex)
833
821
  }
834
822
 
835
- return store.get(recordId)
823
+ return null
836
824
  }
837
825
 
838
826
  /**
@@ -846,11 +834,13 @@ class GridView extends Component {
846
834
  }
847
835
 
848
836
  /**
849
- * @param {Object} record
837
+ * @param {Number} rowIndex
850
838
  * @returns {String}
851
839
  */
852
- getRowId(record) {
853
- return `${this.id}__tr__${record[this.store.getKeyProperty()]}`
840
+ getRowId(rowIndex) {
841
+ let me = this;
842
+
843
+ return `${me.id}__row-${rowIndex % (me.availableRows + 2 * me.bufferRowRange)}`
854
844
  }
855
845
 
856
846
  /**
@@ -951,7 +941,7 @@ class GridView extends Component {
951
941
  needsUpdate = false,
952
942
  rowIndex = me.store.indexOf(record),
953
943
  {selectionModel} = me,
954
- column, needsCellUpdate;
944
+ column, needsCellUpdate, recordId;
955
945
 
956
946
  if (fieldNames.includes(me.colspanField)) {
957
947
  me.vdom.cn[rowIndex] = me.createRow({record, rowIndex});
@@ -971,7 +961,9 @@ class GridView extends Component {
971
961
  fields.forEach(field => {
972
962
  if (field.name === me.selectedRecordField) {
973
963
  if (selectionModel.ntype === 'selection-grid-rowmodel') {
974
- selectionModel[field.value ? 'select' : 'deselect'](me.getRowId(record))
964
+ recordId = record[me.store.getKeyProperty()];
965
+
966
+ selectionModel[field.value ? 'selectRow' : 'deselectRow'](recordId)
975
967
  }
976
968
  } else {
977
969
  needsCellUpdate = me.updateCellNode(record, field.name);
@@ -1040,10 +1032,10 @@ class GridView extends Component {
1040
1032
  */
1041
1033
  updateCellNode(record, dataField) {
1042
1034
  let me = this,
1043
- cellId = me.getCellId(record, dataField),
1035
+ rowIndex = me.store.indexOf(record),
1036
+ cellId = me.getCellId(rowIndex, dataField),
1044
1037
  cellNode = VDomUtil.find(me.vdom, cellId),
1045
1038
  needsUpdate = false,
1046
- rowIndex = me.store.indexOf(record),
1047
1039
  cellStyle, cellVdom, column, columnIndex;
1048
1040
 
1049
1041
  // The vdom might not exist yet => nothing to do in this case
@@ -63,7 +63,7 @@ class AnimatedChange extends Column {
63
63
  // Wait for the next animation frame
64
64
  await me.timeout(20);
65
65
 
66
- cellId = view.getCellId(record, me.dataField);
66
+ cellId = view.getCellId(me.parent.store.indexOf(record), me.dataField);
67
67
  node = VdomUtil.find(view.vdom, cellId)?.vdom;
68
68
 
69
69
  if (node) {
@@ -38,7 +38,12 @@ class Component extends Column {
38
38
  * @member {String} type='component'
39
39
  * @protected
40
40
  */
41
- type: 'component'
41
+ type: 'component',
42
+ /**
43
+ * Set this config to true, in case you want to use 'bind' inside your cell based component.
44
+ * @member {Boolean} useBindings=false
45
+ */
46
+ useBindings: false
42
47
  }
43
48
 
44
49
  /**
@@ -104,13 +109,17 @@ class Component extends Column {
104
109
  windowId
105
110
  });
106
111
 
112
+ // We need to ensure that wrapped components always get the same index-based id.
113
+ if (!component.vdom.id) {
114
+ component.vdom.id = id + '__wrapper'
115
+ }
116
+
107
117
  me.map.set(id, component)
108
118
  }
109
119
 
110
- // The componentConfig can contain bindings into a view controller, e.g. a button handler = 'editButtonHandler'
111
- // componentConfig.set(component) can revert these, so we need a parseConfig() for each change.
112
- view.getController() ?.parseConfig(component);
113
- view.getStateProvider()?.parseConfig(component);
120
+ if (me.useBindings) {
121
+ view.getStateProvider()?.parseConfig(component)
122
+ }
114
123
 
115
124
  view.updateDepth = -1;
116
125
 
@@ -176,21 +176,7 @@ class Button extends BaseButton {
176
176
  let me = this,
177
177
  {cls} = me;
178
178
 
179
- if (value) {
180
- NeoArray.remove(cls, 'neo-sort-hidden');
181
-
182
- me.addDomListeners({
183
- click: me.onButtonClick,
184
- scope: me
185
- })
186
- } else {
187
- NeoArray.add(cls, 'neo-sort-hidden');
188
-
189
- me.removeDomListeners({
190
- click: me.onButtonClick,
191
- scope: me
192
- })
193
- }
179
+ NeoArray.toggle(cls, 'neo-sort-hidden', !value);
194
180
 
195
181
  me.cls = cls;
196
182
  me.update()
@@ -215,9 +201,10 @@ class Button extends BaseButton {
215
201
  }
216
202
 
217
203
  /**
204
+ * @param {Object} data
218
205
  * @protected
219
206
  */
220
- onButtonClick() {
207
+ onClick(data) {
221
208
  let me = this,
222
209
  map;
223
210
 
@@ -235,7 +222,9 @@ class Button extends BaseButton {
235
222
  }
236
223
  }
237
224
 
238
- me.isSorted = map[me.isSorted + '']
225
+ me.isSorted = map[me.isSorted + ''];
226
+
227
+ super.onClick(data)
239
228
  }
240
229
 
241
230
  /**
@@ -223,6 +223,29 @@ class DomEvents extends Base {
223
223
  Neo.worker.Manager.sendMessage('app', config)
224
224
  }
225
225
 
226
+ /**
227
+ *
228
+ * @param {HTMLElement} node
229
+ * @returns {Object}
230
+ */
231
+ geAriaAttributes(node) {
232
+ let ariaAttributes = {},
233
+ {attributes} = node,
234
+ i = 0,
235
+ len = attributes?.length || 0,
236
+ attribute;
237
+
238
+ for (; i < len; i++) {
239
+ attribute = attributes[i];
240
+
241
+ if (attribute.name.startsWith('aria-')) {
242
+ ariaAttributes[attribute.name.substring(5)] = attribute.value
243
+ }
244
+ }
245
+
246
+ return ariaAttributes
247
+ }
248
+
226
249
  /**
227
250
  * Returns the distance between two points
228
251
  * @param {Number} x1 The X position of the first point
@@ -349,6 +372,7 @@ class DomEvents extends Base {
349
372
  rect = r && this.parseDomRect(r) || {};
350
373
 
351
374
  return {
375
+ aria : this.geAriaAttributes(node),
352
376
  checked : node.checked,
353
377
  childElementCount: node.childElementCount,
354
378
  clientHeight : node.clientHeight,
@@ -369,12 +393,13 @@ class DomEvents extends Base {
369
393
  offsetTop : node.offsetTop,
370
394
  offsetWidth : node.offsetWidth,
371
395
  rect,
396
+ role : node.role,
372
397
  scrollHeight : node.scrollHeight,
373
398
  scrollLeft : node.scrollLeft,
374
399
  scrollTop : node.scrollTop,
375
400
  scrollWidth : node.scrollWidth,
376
401
  style : node.style?.cssText,
377
- tabIndex : node.tabIndex,
402
+ tabIndex : node.getAttribute?.('tabindex') ? node.tabIndex : null,
378
403
  tagName : node.tagName?.toLowerCase()
379
404
  }
380
405
  }
@@ -1,10 +1,9 @@
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';
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';
8
7
 
9
8
  const eventConfigKeys = [
10
9
  'bubble',
@@ -110,8 +109,6 @@ class DomEvent extends Base {
110
109
  listeners = me.items[id]?.[eventName];
111
110
 
112
111
  if (listeners) {
113
- // console.log('fire', eventName, data, listeners, path);
114
-
115
112
  if (Array.isArray(listeners)) {
116
113
  // Stop iteration if a handler returns false
117
114
  listeners.every(listener => {
@@ -142,6 +139,10 @@ class DomEvent extends Base {
142
139
  // Handler needs to know which actual target matched the delegate
143
140
  data.currentTarget = delegationTargetId;
144
141
 
142
+ if (Neo.isString(listener.fn)) {
143
+ me.bindCallback(listener.fn, 'fn', listener.scope, listener)
144
+ }
145
+
145
146
  result = listener.fn.apply(listener.scope || globalThis, [data]);
146
147
 
147
148
  if (!listener.bubble) {
@@ -284,18 +285,18 @@ class DomEvent extends Base {
284
285
  }
285
286
 
286
287
  /**
287
- * @param {Object} config
288
+ * @param {Object} config
288
289
  * @param {Boolean} config.bubble
289
- * @param {String} config.delegate
290
- * @param {String} config.eventName
291
- * @param {String} config.id
290
+ * @param {String} config.delegate
291
+ * @param {String} config.eventName
292
+ * @param {String} config.id
292
293
  * @param {Boolean} config.local
293
- * @param {Number} config.opts
294
- * @param {Number} config.originalConfig
295
- * @param {String} config.ownerId
296
- * @param {Number} config.priority=1
297
- * @param {Object} config.scope
298
- * @param {String} config.vnodeId
294
+ * @param {Number} config.opts
295
+ * @param {Number} config.originalConfig
296
+ * @param {String} config.ownerId
297
+ * @param {Number} config.priority=1
298
+ * @param {Object} config.scope
299
+ * @param {String} config.vnodeId
299
300
  * @returns {Boolean} true if the listener got registered successfully (false in case it was already there)
300
301
  */
301
302
  register(config) {
@@ -306,10 +307,8 @@ class DomEvent extends Base {
306
307
  fnType = typeof opts,
307
308
  fn, listener, listenerConfig, listenerId;
308
309
 
309
- if (fnType === 'function') {
310
+ if (fnType === 'function' || fnType === 'string') {
310
311
  fn = opts
311
- } else if (fnType === 'string') {
312
- fn = resolveCallback(opts, scope).fn
313
312
  } else {
314
313
  fn = opts.fn;
315
314
  scope = opts.scope || scope