neo.mjs 8.10.0 → 8.11.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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.10.0'
23
+ * @member {String} version='8.11.0'
24
24
  */
25
- version: '8.10.0'
25
+ version: '8.11.0'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-01-26",
19
+ "datePublished": "2025-01-28",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v8.10.0'
110
+ html : 'v8.11.0'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.10.0'
23
+ * @member {String} version='8.11.0'
24
24
  */
25
- version: '8.10.0'
25
+ version: '8.11.0'
26
26
  }
27
27
 
28
28
  /**
@@ -100,8 +100,7 @@ class MainContainer extends ConfigurationViewport {
100
100
  style.tableLayout = '';
101
101
  }
102
102
 
103
- me.exampleComponent.style = style;
104
- me.exampleComponent.update()
103
+ me.exampleComponent.style = style
105
104
  }
106
105
  },
107
106
  style: {marginTop: '10px'}
@@ -68,7 +68,7 @@ class MainContainer extends Viewport {
68
68
 
69
69
  store: {
70
70
  autoLoad : true,
71
- keyProperty: 'name',
71
+ keyProperty: 'code',
72
72
  url : '../../resources/examples/data/countries.json',
73
73
 
74
74
  model: {
@@ -12,16 +12,16 @@ class MainStore extends Store {
12
12
  model : Model,
13
13
 
14
14
  data: [
15
- {id: 1, country: 'Germany', firstname: 'Tobias', isOnline: true, lastname: 'Uhlig', luckyNumber: 1, specialDate: '2020-08-10'},
16
- {id: 2, country: 'United States', firstname: 'Rich', isOnline: false, lastname: 'Waters', luckyNumber: 2, specialDate: '2020-08-09'},
17
- {id: 3, country: 'Germany', firstname: 'Nils', isOnline: true, lastname: 'Dehl', luckyNumber: 3, specialDate: '2020-08-09'},
18
- {id: 4, country: 'United States', firstname: 'Gerard', isOnline: true, lastname: 'Horan', luckyNumber: 1, specialDate: '2020-08-10'},
19
- {id: 5, country: 'Slovakia', firstname: 'Jozef', isOnline: false, lastname: 'Sakalos', luckyNumber: 2, specialDate: '2020-08-08'},
20
- {id: 6, country: 'Germany', firstname: 'Bastian', isOnline: false, lastname: 'Haustein', luckyNumber: 3, specialDate: '2020-08-10'},
21
- {id: 7, country: 'United States', firstname: 'Durlabh', isOnline: true, lastname: 'Jain', luckyNumber: 1, specialDate: '2020-08-08'},
22
- {id: 8, country: 'Canada', firstname: 'Kevin', isOnline: true, lastname: 'Cassidy', luckyNumber: 2, specialDate: '2020-08-10'},
23
- {id: 9, country: 'UK', firstname: 'Nikola', isOnline: true, lastname: 'Markovic', luckyNumber: 3, specialDate: '2020-08-09'},
24
- {id: 10, country: 'United States', firstname: 'Hyle', isOnline: false, lastname: 'Campbell', luckyNumber: 1, specialDate: '2020-08-10'}
15
+ {id: 1, country: 'Germany', firstname: 'Tobias', isOnline: true, lastname: 'Uhlig', luckyNumber: 1, specialDate: '2025-08-10'},
16
+ {id: 2, country: 'United States', firstname: 'Rich', isOnline: false, lastname: 'Waters', luckyNumber: 2, specialDate: '2025-08-09'},
17
+ {id: 3, country: 'Germany', firstname: 'Nils', isOnline: true, lastname: 'Dehl', luckyNumber: 3, specialDate: '2025-08-09'},
18
+ {id: 4, country: 'United States', firstname: 'Gerard', isOnline: true, lastname: 'Horan', luckyNumber: 1, specialDate: '2025-08-10'},
19
+ {id: 5, country: 'Slovakia', firstname: 'Jozef', isOnline: false, lastname: 'Sakalos', luckyNumber: 2, specialDate: '2025-08-08'},
20
+ {id: 6, country: 'Germany', firstname: 'Bastian', isOnline: false, lastname: 'Haustein', luckyNumber: 3, specialDate: '2025-08-10'},
21
+ {id: 7, country: 'United States', firstname: 'Durlabh', isOnline: true, lastname: 'Jain', luckyNumber: 1, specialDate: '2025-08-08'},
22
+ {id: 8, country: 'Canada', firstname: 'Kevin', isOnline: true, lastname: 'Cassidy', luckyNumber: 2, specialDate: '2025-08-10'},
23
+ {id: 9, country: 'UK', firstname: 'Nikola', isOnline: true, lastname: 'Markovic', luckyNumber: 3, specialDate: '2025-08-09'},
24
+ {id: 10, country: 'United States', firstname: 'Hyle', isOnline: false, lastname: 'Campbell', luckyNumber: 1, specialDate: '2025-08-10'}
25
25
  ]
26
26
  }
27
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "8.10.0",
3
+ "version": "8.11.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -21,10 +21,10 @@ If you move quickly, you might reach 20,000 or 30,000 delta updates per second.
21
21
  second — but we've never actually hit the limit.
22
22
 
23
23
  <pre data-neo>
24
- import Base from '../container/Base.mjs';
25
- import Helix from '../component/Helix.mjs';
24
+ import Container from '../container/Base.mjs';
25
+ import Helix from '../component/Helix.mjs';
26
26
 
27
- class MainView extends Base {
27
+ class MainView extends Container {
28
28
  static config = {
29
29
  className: 'Benefits.speed.MainView',
30
30
  layout : 'fit',
@@ -65,7 +65,7 @@
65
65
  .neo-button-glyph {
66
66
  color : var(--button-glyph-color);
67
67
  font-size : 12px;
68
- margin : 0 6px 0 0;
68
+ margin : var(--button-glyph-margin);
69
69
  pointer-events: none;
70
70
  z-index : 2;
71
71
  }
@@ -102,21 +102,3 @@
102
102
  }
103
103
  }
104
104
  }
105
-
106
- .neo-selection-cellcolumnmodel,
107
- .neo-selection-cellcolumnrowmodel,
108
- .neo-selection-cellmodel,
109
- .neo-selection-cellrowmodel,
110
- .neo-selection-columnmodel,
111
- .neo-selection-rowmodel {
112
- .neo-grid-row {
113
- &:focus {
114
- outline: 0;
115
- }
116
- .neo-grid-cell {
117
- &:focus {
118
- outline: 0;
119
- }
120
- }
121
- }
122
- }
@@ -1,4 +1,8 @@
1
1
  .neo-table-view {
2
+ &:focus {
3
+ outline: none;
4
+ }
5
+
2
6
  .neo-center {
3
7
  text-align: center;
4
8
  }
@@ -9,8 +13,6 @@
9
13
 
10
14
  .neo-table-row {
11
15
  .neo-table-cell {
12
- position: relative;
13
-
14
16
  &.neo-is-modified {
15
17
  &:after {
16
18
  border-color: transparent var(--table-cell-ismodified-color) transparent transparent;
@@ -73,21 +75,3 @@
73
75
  }
74
76
  }
75
77
  }
76
-
77
- .neo-selection-cellcolumnmodel,
78
- .neo-selection-cellcolumnrowmodel,
79
- .neo-selection-cellmodel,
80
- .neo-selection-cellrowmodel,
81
- .neo-selection-columnmodel,
82
- .neo-selection-rowmodel {
83
- .neo-table-row {
84
- &:focus {
85
- outline: 0;
86
- }
87
- .neo-table-cell {
88
- &:focus {
89
- outline: 0;
90
- }
91
- }
92
- }
93
- }
@@ -147,7 +147,7 @@
147
147
  .neo-button {
148
148
  &.icon-left {
149
149
  .neo-button-glyph {
150
- margin: 0 6px 0 0;
150
+ margin: var(--button-glyph-margin);
151
151
  }
152
152
  }
153
153
  }
@@ -29,6 +29,7 @@ $text-color : #bbb;
29
29
  --button-glyph-color-active : #{$text-color};
30
30
  --button-glyph-color-disabled : #{$text-color};
31
31
  --button-glyph-color-hover : #{$text-color};
32
+ --button-glyph-margin : 1px 6px 0 0;
32
33
  --button-height : initial;
33
34
  --button-margin : 2px;
34
35
  --button-opacity-disabled : var(--neo-disabled-opacity);
@@ -28,6 +28,7 @@ $text-color : #1c60a0;
28
28
  --button-glyph-color-active : #{$text-color};
29
29
  --button-glyph-color-disabled : #{$text-color};
30
30
  --button-glyph-color-hover : #{$text-color};
31
+ --button-glyph-margin : 1px 6px 0 0;
31
32
  --button-height : initial;
32
33
  --button-margin : 2px;
33
34
  --button-opacity-disabled : var(--neo-disabled-opacity);
@@ -39,6 +39,7 @@
39
39
  --button-glyph-color-hover : var(--sem-color-text-neutral-reversed);
40
40
  --button-glyph-color-active : var(--sem-color-text-neutral-reversed);
41
41
  --button-glyph-color-disabled : var(--sem-color-text-neutral-disabled);
42
+ --button-glyph-margin : 1px 6px 0 0;
42
43
 
43
44
  --button-background-image : none;
44
45
  --button-background-gradient-end : #323536;
@@ -262,12 +262,12 @@ const DefaultConfig = {
262
262
  useVdomWorker: true,
263
263
  /**
264
264
  * buildScripts/injectPackageVersion.mjs will update this value
265
- * @default '8.10.0'
265
+ * @default '8.11.0'
266
266
  * @memberOf! module:Neo
267
267
  * @name config.version
268
268
  * @type String
269
269
  */
270
- version: '8.10.0'
270
+ version: '8.11.0'
271
271
  };
272
272
 
273
273
  Object.assign(DefaultConfig, {
package/src/Main.mjs CHANGED
@@ -158,13 +158,19 @@ class Main extends core.Base {
158
158
  /**
159
159
  * Request specific accessible window attributes by path into the app worker.
160
160
  * Keep in mind that this excludes anything DOM related or instances.
161
- * Example: Neo.Main.getByPath({path: 'navigator.language'}).then(data => {})
161
+ * In case your path matches a method, you can also pass params for it.
162
+ * @example:
163
+ * Neo.Main.getByPath({path: 'navigator.language'}).then(data => {})
164
+ * @example:
165
+ * Neo.Main.getByPath({path: 'CSS.supports', params: ['display: flex']}).then(data => {})
162
166
  * @param {Object} data
167
+ * @param {Array} data.params=[]
163
168
  * @param {String} data.path
164
169
  * @returns {*}
165
170
  */
166
- getByPath(data) {
167
- return Neo.nsWithArrays(data.path)
171
+ getByPath({params=[], path}) {
172
+ let target = Neo.nsWithArrays(path);
173
+ return Neo.isFunction(target) ? target(...params) : target
168
174
  }
169
175
 
170
176
  /**
@@ -450,10 +450,7 @@ class ComboBox extends Picker {
450
450
  fireChangeEvent(value, oldValue) {
451
451
  let me = this,
452
452
  FormContainer = Neo.form?.Container,
453
- params;
454
-
455
- if (!(me.forceSelection && !value)) {
456
- params = {component: me, oldValue, value};
453
+ params = {component: me, oldValue, value};
457
454
 
458
455
  me.fire('change', params);
459
456
 
@@ -464,7 +461,6 @@ class ComboBox extends Picker {
464
461
  }
465
462
  })
466
463
  }
467
- }
468
464
  }
469
465
 
470
466
  /**
@@ -420,36 +420,33 @@ class DomAccess extends Base {
420
420
 
421
421
  /**
422
422
  * Returns the attributes for a given dom node id
423
- * @param {Object} data
424
- * @param {Array|String} data.id either an id or an array of ids
425
- * @param {Array|String} data.attributes either an attribute or an array of attributes
423
+ * @param {Object} data
424
+ * @param {String|String[]} data.attributes either an attribute or an array of attributes
425
+ * @param {String|String[]} data.id either an id or an array of ids
426
426
  * @returns {Array|Object} In case id is an array, an array of attribute objects is returned, otherwise an object
427
427
  */
428
- getAttributes(data) {
428
+ getAttributes({attributes, id}) {
429
429
  let returnData;
430
430
 
431
- if (Array.isArray(data.id)) {
431
+ if (Array.isArray(id)) {
432
432
  returnData = [];
433
433
 
434
- data.id.forEach(id => {
435
- returnData.push(this.getAttributes({
436
- attributes: data.attributes,
437
- id : id
438
- }))
434
+ id.forEach(id => {
435
+ returnData.push(this.getAttributes({attributes, id}))
439
436
  })
440
437
  } else {
441
- let node = this.getElementOrBody(data.id);
438
+ let node = this.getElementOrBody(id);
442
439
 
443
440
  returnData = {};
444
441
 
445
442
  if (node) {
446
- if (!Array.isArray(data.attributes)) {
447
- data.attributes = [data.attributes];
448
-
449
- data.attributes.forEach(attribute => {
450
- returnData[attribute] = node[attribute]
451
- })
443
+ if (!Array.isArray(attributes)) {
444
+ attributes = [attributes]
452
445
  }
446
+
447
+ attributes.forEach(attribute => {
448
+ returnData[attribute] = node[attribute]
449
+ })
453
450
  }
454
451
  }
455
452
 
@@ -14,6 +14,14 @@ class BaseModel extends Model {
14
14
  */
15
15
  className: 'Neo.selection.table.BaseModel'
16
16
  }
17
+
18
+ /**
19
+ * Convenience shortcut
20
+ * @member {String[]} dataFields
21
+ */
22
+ get dataFields() {
23
+ return this.view.parent.columns.map(c => c.dataField)
24
+ }
17
25
  }
18
26
 
19
27
  export default Neo.setupClass(BaseModel);
@@ -1,6 +1,5 @@
1
- import CellModel from './CellModel.mjs';
2
- import ColumnModel from './ColumnModel.mjs';
3
- import VDomUtil from '../../util/VDom.mjs';
1
+ import CellModel from './CellModel.mjs';
2
+ import VDomUtil from '../../util/VDom.mjs';
4
3
 
5
4
  /**
6
5
  * @class Neo.selection.table.CellColumnModel
@@ -41,7 +40,7 @@ class CellColumnModel extends CellModel {
41
40
  deselectAllCells(silent) {
42
41
  let me = this,
43
42
  cellIds = [...me.selectedColumnCellIds],
44
- {view} = me;
43
+ {view} = me
45
44
 
46
45
  cellIds.forEach(cellId => {
47
46
  me.deselect(cellId, true, me.selectedColumnCellIds, me.selectedColumnCellCls)
@@ -54,14 +53,15 @@ class CellColumnModel extends CellModel {
54
53
  * @param {Object} data
55
54
  */
56
55
  onCellClick(data) {
57
- let me = this,
58
- {headerToolbar} = me.view.parent,
59
- id = data.data.currentTarget,
60
- columnNodeIds, index;
56
+ let me = this,
57
+ {view} = me,
58
+ cellId = data.data.currentTarget,
59
+ columnNodeIds, dataField, index;
61
60
 
62
- if (id) {
63
- index = ColumnModel.getColumnIndex(id, headerToolbar.items);
64
- columnNodeIds = VDomUtil.getColumnNodesIds(me.view.vdom, index);
61
+ if (cellId) {
62
+ dataField = view.getDataField(cellId);
63
+ index = view.getColumn(dataField, true);
64
+ columnNodeIds = VDomUtil.getColumnNodesIds(view.vdom, index);
65
65
 
66
66
  me.deselectAllCells(true);
67
67
  me.select(columnNodeIds, me.selectedColumnCellIds, me.selectedColumnCellCls)
@@ -71,29 +71,31 @@ class CellColumnModel extends CellModel {
71
71
  }
72
72
 
73
73
  /**
74
- * @param {Object} data
75
74
  * @param {Number} step
76
75
  */
77
- onNavKeyColumn(data, step) {
78
- let me = this,
79
- idArray = ColumnModel.getCellId(data.path).split('__'),
80
- currentColumn = idArray[2],
81
- {view} = me,
82
- dataFields = view.parent.columns.map(c => c.dataField),
83
- newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length,
84
- columnNodeIds, tbodyNode;
85
-
86
- while (newIndex < 0) {
87
- newIndex += dataFields.length
76
+ onNavKeyColumn(step) {
77
+ let me = this,
78
+ {dataFields, view} = me,
79
+ columnNodeIds, currentColumn, index;
80
+
81
+ if (me.hasSelection()) {
82
+ currentColumn = view.getDataField(me.items[0])
83
+ } else {
84
+ currentColumn = dataFields[0]
85
+ }
86
+
87
+ index = (dataFields.indexOf(currentColumn) + step) % dataFields.length;
88
+
89
+ while (index < 0) {
90
+ index += dataFields.length
88
91
  }
89
92
 
90
- tbodyNode = VDomUtil.find(me.view.vdom, {tag: 'tbody'}).vdom;
91
- columnNodeIds = VDomUtil.getColumnNodesIds(tbodyNode, newIndex);
93
+ columnNodeIds = VDomUtil.getColumnNodesIds(view.vdom, index);
92
94
 
93
95
  me.deselectAllCells(true);
94
96
  me.select(columnNodeIds, me.selectedColumnCellIds, me.selectedColumnCellCls);
95
97
 
96
- super.onNavKeyColumn(data, step)
98
+ super.onNavKeyColumn(step)
97
99
  }
98
100
 
99
101
  /**
@@ -1,5 +1,4 @@
1
1
  import CellRowModel from './CellRowModel.mjs';
2
- import ColumnModel from './ColumnModel.mjs';
3
2
  import VDomUtil from '../../util/VDom.mjs';
4
3
 
5
4
  /**
@@ -54,14 +53,15 @@ class CellColumnRowModel extends CellRowModel {
54
53
  * @param {Object} data
55
54
  */
56
55
  onCellClick(data) {
57
- let me = this,
58
- {headerToolbar} = me.view.parent,
59
- id = data.data.currentTarget,
60
- columnNodeIds, index;
56
+ let me = this,
57
+ {view} = me,
58
+ cellId = data.data.currentTarget,
59
+ columnNodeIds, dataField, index;
61
60
 
62
- if (id) {
63
- index = ColumnModel.getColumnIndex(id, headerToolbar.items);
64
- columnNodeIds = VDomUtil.getColumnNodesIds(me.view.vdom, index);
61
+ if (cellId) {
62
+ dataField = view.getDataField(cellId);
63
+ index = view.getColumn(dataField, true);
64
+ columnNodeIds = VDomUtil.getColumnNodesIds(view.vdom, index);
65
65
 
66
66
  me.deselectAllCells(true);
67
67
  me.select(columnNodeIds, me.selectedColumnCellIds, me.selectedColumnCellCls)
@@ -71,29 +71,31 @@ class CellColumnRowModel extends CellRowModel {
71
71
  }
72
72
 
73
73
  /**
74
- * @param {Object} data
75
74
  * @param {Number} step
76
75
  */
77
- onNavKeyColumn(data, step) {
78
- let me = this,
79
- idArray = ColumnModel.getCellId(data.path).split('__'),
80
- currentColumn = idArray[2],
81
- {view} = me,
82
- dataFields = view.parent.columns.map(c => c.dataField),
83
- newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length,
84
- columnNodeIds, tbodyNode;
85
-
86
- while (newIndex < 0) {
87
- newIndex += dataFields.length
76
+ onNavKeyColumn(step) {
77
+ let me = this,
78
+ {dataFields, view} = me,
79
+ columnNodeIds, currentColumn, index;
80
+
81
+ if (me.hasSelection()) {
82
+ currentColumn = view.getDataField(me.items[0])
83
+ } else {
84
+ currentColumn = dataFields[0]
85
+ }
86
+
87
+ index = (dataFields.indexOf(currentColumn) + step) % dataFields.length;
88
+
89
+ while (index < 0) {
90
+ index += dataFields.length
88
91
  }
89
92
 
90
- tbodyNode = VDomUtil.find(me.view.vdom, {tag: 'tbody'}).vdom;
91
- columnNodeIds = VDomUtil.getColumnNodesIds(tbodyNode, newIndex);
93
+ columnNodeIds = VDomUtil.getColumnNodesIds(view.vdom, index);
92
94
 
93
95
  me.deselectAllCells(true);
94
96
  me.select(columnNodeIds, me.selectedColumnCellIds, me.selectedColumnCellCls);
95
97
 
96
- super.onNavKeyColumn(data, step)
98
+ super.onNavKeyColumn(step)
97
99
  }
98
100
 
99
101
  /**
@@ -54,76 +54,80 @@ class CellModel extends BaseModel {
54
54
  * @param {Object} data
55
55
  */
56
56
  onKeyDownDown(data) {
57
- this.onNavKeyRow(data, 1)
57
+ this.onNavKeyRow(1)
58
58
  }
59
59
 
60
60
  /**
61
61
  * @param {Object} data
62
62
  */
63
63
  onKeyDownLeft(data) {
64
- this.onNavKeyColumn(data, -1)
64
+ this.onNavKeyColumn(-1)
65
65
  }
66
66
 
67
67
  /**
68
68
  * @param {Object} data
69
69
  */
70
70
  onKeyDownRight(data) {
71
- this.onNavKeyColumn(data, 1)
71
+ this.onNavKeyColumn(1)
72
72
  }
73
73
 
74
74
  /**
75
75
  * @param {Object} data
76
76
  */
77
77
  onKeyDownUp(data) {
78
- this.onNavKeyRow(data, -1)
78
+ this.onNavKeyRow(-1)
79
79
  }
80
80
 
81
81
  /**
82
- * @param {Object} data
83
82
  * @param {Number} step
84
83
  */
85
- onNavKeyColumn(data, step) {
86
- let me = this,
87
- {view} = me,
88
- idArray = data.path[0].id.split('__'),
89
- currentColumn = idArray[2],
90
- dataFields = view.parent.columns.map(c => c.dataField),
91
- newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length,
92
- id;
84
+ onNavKeyColumn(step) {
85
+ let me = this,
86
+ {dataFields, view} = me,
87
+ {store} = view,
88
+ currentColumn, newIndex, record;
89
+
90
+ if (me.hasSelection()) {
91
+ currentColumn = view.getDataField(me.items[0]);
92
+ record = view.getRecord(me.items[0])
93
+ } else {
94
+ currentColumn = dataFields[0];
95
+ record = store.getAt(0)
96
+ }
97
+
98
+ newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length;
93
99
 
94
100
  while (newIndex < 0) {
95
101
  newIndex += dataFields.length
96
102
  }
97
103
 
98
- idArray[2] = dataFields[newIndex];
99
- id = idArray.join('__');
100
-
101
- me.select(id);
102
- view.focus(id)
104
+ me.select(view.getCellId(record, dataFields[newIndex]))
103
105
  }
104
106
 
105
107
  /**
106
- * @param {Object} data
107
108
  * @param {Number} step
108
109
  */
109
- onNavKeyRow(data, step) {
110
- let me = this,
111
- {view} = me,
112
- {store} = view,
113
- idArray = data.path[0].id.split('__'),
114
- recordId = idArray[1],
115
- newIndex = (store.indexOf(recordId) + step) % store.getCount(),
116
- id;
110
+ onNavKeyRow(step) {
111
+ let me = this,
112
+ {view} = me,
113
+ {store} = view,
114
+ currentIndex = 0,
115
+ dataField, newIndex;
116
+
117
+ if (me.hasSelection()) {
118
+ currentIndex = store.indexOf(view.getRecord(me.items[0]));
119
+ dataField = view.getDataField(me.items[0])
120
+ } else {
121
+ dataField = me.dataFields[0]
122
+ }
123
+
124
+ newIndex = (currentIndex + step) % store.getCount();
117
125
 
118
126
  while (newIndex < 0) {
119
127
  newIndex += store.getCount()
120
128
  }
121
129
 
122
- idArray[1] = store.getKeyAt(newIndex);
123
- id = idArray.join('__');
124
-
125
- me.select(id);
126
- view.focus(id)
130
+ me.select(view.getCellId(store.getAt(newIndex), dataField))
127
131
  }
128
132
 
129
133
  /**
@@ -133,13 +137,14 @@ class CellModel extends BaseModel {
133
137
  super.register(component);
134
138
 
135
139
  let me = this,
136
- {id, view} = me;
140
+ {id, view} = me,
141
+ scope = id;
137
142
 
138
143
  view.keys?._keys.push(
139
- {fn: 'onKeyDownDown' ,key: 'Down' ,scope: id},
140
- {fn: 'onKeyDownLeft' ,key: 'Left' ,scope: id},
141
- {fn: 'onKeyDownRight' ,key: 'Right' ,scope: id},
142
- {fn: 'onKeyDownUp' ,key: 'Up' ,scope: id}
144
+ {fn: 'onKeyDownDown' ,key: 'Down' ,scope},
145
+ {fn: 'onKeyDownLeft' ,key: 'Left' ,scope},
146
+ {fn: 'onKeyDownRight' ,key: 'Right' ,scope},
147
+ {fn: 'onKeyDownUp' ,key: 'Up' ,scope}
143
148
  )
144
149
  }
145
150
 
@@ -147,13 +152,15 @@ class CellModel extends BaseModel {
147
152
  *
148
153
  */
149
154
  unregister() {
150
- let {id, view} = this;
155
+ let me = this,
156
+ {id, view} = me,
157
+ scope = id;
151
158
 
152
159
  view.keys?.removeKeys([
153
- {fn: 'onKeyDownDown' ,key: 'Down' ,scope: id},
154
- {fn: 'onKeyDownLeft' ,key: 'Left' ,scope: id},
155
- {fn: 'onKeyDownRight' ,key: 'Right' ,scope: id},
156
- {fn: 'onKeyDownUp' ,key: 'Up' ,scope: id}
160
+ {fn: 'onKeyDownDown' ,key: 'Down' ,scope},
161
+ {fn: 'onKeyDownLeft' ,key: 'Left' ,scope},
162
+ {fn: 'onKeyDownRight' ,key: 'Right' ,scope},
163
+ {fn: 'onKeyDownUp' ,key: 'Up' ,scope}
157
164
  ]);
158
165
 
159
166
  super.unregister()
@@ -1,7 +1,5 @@
1
1
  import CellModel from './CellModel.mjs';
2
2
  import NeoArray from '../../util/Array.mjs';
3
- import RowModel from './RowModel.mjs';
4
- import VDomUtil from '../../util/VDom.mjs';
5
3
 
6
4
  /**
7
5
  * @class Neo.selection.table.CellRowModel
@@ -71,39 +69,39 @@ class CellRowModel extends CellModel {
71
69
  * @param {Object} data
72
70
  */
73
71
  onCellClick(data) {
74
- let me = this,
75
- node = RowModel.getRowNode(data.data.path), // we could add a separate export for this method
76
- id = node?.id;
72
+ let me = this,
73
+ record = me.view.getRecord(data.data.currentTarget),
74
+ rowId = me.view.getRowId(record);
77
75
 
78
- if (id) {
76
+ if (rowId) {
79
77
  me.deselectAllRows(true);
80
- me.selectRow(id)
78
+ me.selectRow(rowId)
81
79
  }
82
80
 
83
81
  super.onCellClick(data)
84
82
  }
85
83
 
86
84
  /**
87
- * @param {Object} data
88
85
  * @param {Number} step
89
86
  */
90
- onNavKeyRow(data, step) {
91
- super.onNavKeyRow(data, step);
92
-
93
- let me = this,
94
- node = RowModel.getRowNode(data.path),
95
- {view} = me,
96
- {store} = view,
97
- vdomNode = VDomUtil.find(view.vdom, node.id),
98
- newIndex = (vdomNode.index + step) % store.getCount(),
99
- parentNode = vdomNode.parentNode,
87
+ onNavKeyRow(step) {
88
+ super.onNavKeyRow(step);
89
+
90
+ let me = this,
91
+ {view} = me,
92
+ {store} = view,
93
+ countRecords = store.getCount(),
94
+ rowId = me.selectedRowIds[0] || view.getRowId(store.getAt(0)),
95
+ record = view.getRecord(rowId),
96
+ index = store.indexOf(record),
97
+ newIndex = (index + step) % countRecords,
100
98
  id;
101
99
 
102
100
  while (newIndex < 0) {
103
- newIndex += store.getCount()
101
+ newIndex += countRecords
104
102
  }
105
103
 
106
- id = parentNode.cn[newIndex].id;
104
+ id = view.getRowId(store.getAt(newIndex));
107
105
 
108
106
  if (id) {
109
107
  me.deselectAllRows(true);
@@ -44,51 +44,19 @@ class ColumnModel extends BaseModel {
44
44
  super.destroy(...args)
45
45
  }
46
46
 
47
- /**
48
- * @param {Object} eventPath
49
- * @returns {String|null} cellId
50
- */
51
- static getCellId(eventPath) {
52
- let id = null,
53
- i = 0,
54
- len = eventPath.length;
55
-
56
- for (; i < len; i++) {
57
- if (eventPath[i].tagName === 'td') {
58
- id = eventPath[i].id;
59
- break
60
- }
61
- }
62
-
63
- return id
64
- }
65
-
66
- /**
67
- * todo: move to table.Container or view
68
- * @param {String} cellId
69
- * @param {Array} columns
70
- * @returns {Number} index
71
- */
72
- static getColumnIndex(cellId, columns) {
73
- let idArray = cellId.split('__'),
74
- currentColumn = idArray[2],
75
- dataFields = columns.map(c => c.dataField);
76
-
77
- return dataFields.indexOf(currentColumn)
78
- }
79
-
80
47
  /**
81
48
  * @param {Object} data
82
49
  */
83
50
  onCellClick(data) {
84
- let me = this,
85
- {headerToolbar} = me.view.parent,
86
- id = data.data.currentTarget,
87
- columnNodeIds, index;
51
+ let me = this,
52
+ {view} = me,
53
+ cellId = data.data.currentTarget,
54
+ columnNodeIds, dataField, index;
88
55
 
89
- if (id) {
90
- index = ColumnModel.getColumnIndex(id, headerToolbar.items);
91
- columnNodeIds = VDomUtil.getColumnNodesIds(me.view.vdom, index);
56
+ if (cellId) {
57
+ dataField = view.getDataField(cellId);
58
+ index = view.getColumn(dataField, true);
59
+ columnNodeIds = VDomUtil.getColumnNodesIds(view.vdom, index);
92
60
 
93
61
  me.select(columnNodeIds)
94
62
  }
@@ -98,41 +66,39 @@ class ColumnModel extends BaseModel {
98
66
  * @param {Object} data
99
67
  */
100
68
  onKeyDownLeft(data) {
101
- this.onNavKeyColumn(data, -1)
69
+ this.onNavKeyColumn(-1)
102
70
  }
103
71
 
104
72
  /**
105
73
  * @param {Object} data
106
74
  */
107
75
  onKeyDownRight(data) {
108
- this.onNavKeyColumn(data, 1)
76
+ this.onNavKeyColumn(1)
109
77
  }
110
78
 
111
79
  /**
112
- * @param {Object} data
113
80
  * @param {Number} step
114
81
  */
115
- onNavKeyColumn(data, step) {
116
- let me = this,
117
- idArray = ColumnModel.getCellId(data.path).split('__'),
118
- currentColumn = idArray[2],
119
- {view} = me,
120
- dataFields = view.parent.columns.map(c => c.dataField),
121
- newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length,
122
- columnNodeIds, id, tbodyNode;
123
-
124
- while (newIndex < 0) {
125
- newIndex += dataFields.length
82
+ onNavKeyColumn(step) {
83
+ let me = this,
84
+ {dataFields, view} = me,
85
+ columnNodeIds, currentColumn, index;
86
+
87
+ if (me.hasSelection()) {
88
+ currentColumn = view.getDataField(me.items[0])
89
+ } else {
90
+ currentColumn = dataFields[0]
126
91
  }
127
92
 
128
- idArray[2] = dataFields[newIndex];
129
- id = idArray.join('__');
93
+ index = (dataFields.indexOf(currentColumn) + step) % dataFields.length;
94
+
95
+ while (index < 0) {
96
+ index += dataFields.length
97
+ }
130
98
 
131
- tbodyNode = VDomUtil.find(me.view.vdom, {tag: 'tbody'}).vdom;
132
- columnNodeIds = VDomUtil.getColumnNodesIds(tbodyNode, newIndex);
99
+ columnNodeIds = VDomUtil.getColumnNodesIds(view.vdom, index);
133
100
 
134
- me.select(columnNodeIds);
135
- view.focus(id) // we have to focus one cell to ensure the keynav keeps working
101
+ me.select(columnNodeIds)
136
102
  }
137
103
 
138
104
  /**
@@ -1,5 +1,4 @@
1
1
  import BaseModel from './BaseModel.mjs';
2
- import VDomUtil from '../../util/VDom.mjs';
3
2
 
4
3
  /**
5
4
  * @class Neo.selection.table.RowModel
@@ -44,76 +43,45 @@ class RowModel extends BaseModel {
44
43
  super.destroy(...args)
45
44
  }
46
45
 
47
- /**
48
- * Finds the matching table row for a given row index
49
- * @param {Number} index row index
50
- * @returns {String|null} The table row node id
51
- */
52
- getRowId(index) {
53
- if (index < 0 || this.view.store.getCount() < index) {
54
- return null
55
- }
56
-
57
- return this.view.vdom.cn[0].cn[1].cn[index].id
58
- }
59
-
60
- /**
61
- * Finds the matching table row for a given event path
62
- * @param {Object} path The event path
63
- * @returns {Object|null} The node containing the table row class or null
64
- * @protected
65
- */
66
- static getRowNode(path) {
67
- let i = 0,
68
- len = path.length,
69
- node = null;
70
-
71
- for (; i < len; i++) {
72
- if (path[i].cls.includes('neo-table-row')) {
73
- node = path[i]
74
- }
75
- }
76
-
77
- return node
78
- }
79
-
80
46
  /**
81
47
  * @param {Object} data
82
48
  */
83
49
  onKeyDownDown(data) {
84
- this.onNavKeyRow(data, 1)
50
+ this.onNavKeyRow(1)
85
51
  }
86
52
 
87
53
  /**
88
54
  * @param {Object} data
89
55
  */
90
56
  onKeyDownUp(data) {
91
- this.onNavKeyRow(data, -1)
57
+ this.onNavKeyRow(-1)
92
58
  }
93
59
 
94
60
  /**
95
- * @param {Object} data
96
61
  * @param {Number} step
97
62
  */
98
- onNavKeyRow(data, step) {
63
+ onNavKeyRow(step) {
99
64
  let me = this,
100
- node = RowModel.getRowNode(data.path),
101
65
  {view} = me,
102
66
  {store} = view,
103
- vdomNode = VDomUtil.find(view.vdom, node.id),
104
- newIndex = (vdomNode.index + step) % store.getCount(),
105
- {parentNode} = vdomNode,
106
- id;
67
+ currentIndex = 0,
68
+ newIndex, newRecord, rowId;
69
+
70
+ if (me.hasSelection()) {
71
+ currentIndex = store.indexOf(view.getRecordByRowId(me.items[0]))
72
+ }
73
+
74
+ newIndex = (currentIndex + step) % store.getCount();
107
75
 
108
76
  while (newIndex < 0) {
109
77
  newIndex += store.getCount()
110
78
  }
111
79
 
112
- id = parentNode.cn[newIndex].id;
80
+ newRecord = store.getAt(newIndex);
81
+ rowId = view.getRowId(newRecord);
113
82
 
114
- if (id) {
115
- me.select(id);
116
- view.focus(id);
83
+ if (rowId) {
84
+ me.select(rowId);
117
85
 
118
86
  view.fire('select', {
119
87
  record: store.getAt(newIndex)
@@ -134,7 +102,7 @@ class RowModel extends BaseModel {
134
102
  me.toggleSelection(id);
135
103
 
136
104
  isSelected = me.isSelected(id);
137
- record = view.store.getAt(VDomUtil.find(view.vdom, id).index);
105
+ record = view.getRecord(id);
138
106
 
139
107
  !isSelected && view.onDeselect?.(record);
140
108
 
@@ -67,7 +67,7 @@ class View extends Component {
67
67
  * @member {Object} _vdom={tag: 'tbody', cn : []}
68
68
  */
69
69
  _vdom:
70
- {tag: 'tbody', cn: []}
70
+ {tag: 'tbody', tabIndex: -1, cn: []}
71
71
  }
72
72
 
73
73
  /**
@@ -189,11 +189,10 @@ class View extends Component {
189
189
  }
190
190
 
191
191
  cellConfig = {
192
- tag : 'td',
193
- id : cellId,
194
- cls : cellCls,
195
- style : rendererOutput.style || {},
196
- tabIndex: '-1'
192
+ tag : 'td',
193
+ id : cellId,
194
+ cls : cellCls,
195
+ style: rendererOutput.style || {}
197
196
  };
198
197
 
199
198
  if (colspan && Object.keys(colspan).includes(dataField)) {
@@ -260,11 +259,10 @@ class View extends Component {
260
259
  }
261
260
 
262
261
  tableRow = {
263
- tag : 'tr',
262
+ tag: 'tr',
264
263
  id,
265
- cls : trCls,
266
- cn : [],
267
- tabIndex: '-1'
264
+ cls: trCls,
265
+ cn : []
268
266
  };
269
267
 
270
268
  for (i=0; i < colCount; i++) {
@@ -409,6 +407,14 @@ class View extends Component {
409
407
  return null
410
408
  }
411
409
 
410
+ /**
411
+ * @param {String} cellId
412
+ * @returns {String}
413
+ */
414
+ getDataField(cellId) {
415
+ return cellId.split('__')[2]
416
+ }
417
+
412
418
  /**
413
419
  * Get the matching record by passing a row id, a cell id or an id inside a table cell.
414
420
  * @param {String} nodeId
@@ -292,6 +292,76 @@ class Button extends BaseButton {
292
292
  return data.value
293
293
  }
294
294
 
295
+ /**
296
+ * @param {Object} data
297
+ */
298
+ changeFilterOperator(data) {
299
+ let me = this,
300
+ tableContainer = me.up('table-container'),
301
+ store = tableContainer?.store,
302
+ operator = data.value,
303
+ filter, filters;
304
+
305
+ if (store) {
306
+ filter = store.getFilter(me.dataField);
307
+
308
+ if (!filter) {
309
+ filters = store.filters;
310
+
311
+ filters.push({
312
+ property: me.dataField,
313
+ operator,
314
+ value : null,
315
+ ...me.filterConfig
316
+ });
317
+
318
+ store.filters = filters
319
+ } else {
320
+ filter.operator = operator
321
+ }
322
+ }
323
+ }
324
+
325
+ /**
326
+ * @param {Object} data
327
+ */
328
+ changeFilterValue(data) {
329
+ let me = this,
330
+ tableContainer = me.up('table-container'),
331
+ store = tableContainer?.store,
332
+ {value} = data,
333
+ field, filter, filters, model;
334
+
335
+ if (store) {
336
+ filter = store.getFilter(me.dataField);
337
+ model = store.model;
338
+ field = model && model.getField(me.dataField);
339
+
340
+ if (value && field.type.toLowerCase() === 'date') {
341
+ value = new Date(value)
342
+ }
343
+
344
+ if (Neo.isRecord(value)) {
345
+ value = value[me.filterField.displayField]
346
+ }
347
+
348
+ if (!filter) {
349
+ filters = store.filters;
350
+
351
+ filters.push({
352
+ property: me.dataField,
353
+ operator: 'like',
354
+ value,
355
+ ...me.filterConfig
356
+ });
357
+
358
+ store.filters = filters
359
+ } else {
360
+ filter.value = value
361
+ }
362
+ }
363
+ }
364
+
295
365
  /**
296
366
  *
297
367
  */
@@ -411,72 +481,6 @@ class Button extends BaseButton {
411
481
  me.style = style
412
482
  }
413
483
 
414
- /**
415
- * @param {Object} data
416
- */
417
- changeFilterOperator(data) {
418
- let me = this,
419
- tableContainer = me.up('table-container'),
420
- store = tableContainer?.store,
421
- operator = data.value,
422
- filter, filters;
423
-
424
- if (store) {
425
- filter = store.getFilter(me.dataField);
426
-
427
- if (!filter) {
428
- filters = store.filters;
429
-
430
- filters.push({
431
- property: me.dataField,
432
- operator,
433
- value : null,
434
- ...me.filterConfig
435
- });
436
-
437
- store.filters = filters
438
- } else {
439
- filter.operator = operator
440
- }
441
- }
442
- }
443
-
444
- /**
445
- * @param {Object} data
446
- */
447
- changeFilterValue(data) {
448
- let me = this,
449
- tableContainer = me.up('table-container'),
450
- store = tableContainer?.store,
451
- {value} = data,
452
- field, filter, filters, model;
453
-
454
- if (store) {
455
- filter = store.getFilter(me.dataField);
456
- model = store.model;
457
- field = model && model.getField(me.dataField);
458
-
459
- if (value && field.type.toLowerCase() === 'date') {
460
- value = new Date(value)
461
- }
462
-
463
- if (!filter) {
464
- filters = store.filters;
465
-
466
- filters.push({
467
- property: me.dataField,
468
- operator: 'like',
469
- value,
470
- ...me.filterConfig
471
- });
472
-
473
- store.filters = filters
474
- } else {
475
- filter.value = value
476
- }
477
- }
478
- }
479
-
480
484
  /**
481
485
  * @protected
482
486
  */