neo.mjs 8.0.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/portal/index.html +1 -1
  3. package/apps/portal/view/about/Container.mjs +0 -2
  4. package/apps/portal/view/about/MemberContainer.mjs +1 -20
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -5
  6. package/examples/ConfigurationViewport.mjs +37 -32
  7. package/examples/ServiceWorker.mjs +2 -2
  8. package/examples/grid/cellEditing/MainContainer.mjs +175 -0
  9. package/examples/grid/cellEditing/MainContainerStateProvider.mjs +62 -0
  10. package/examples/grid/cellEditing/MainModel.mjs +30 -0
  11. package/examples/grid/cellEditing/MainStore.mjs +54 -0
  12. package/examples/grid/cellEditing/app.mjs +6 -0
  13. package/examples/grid/cellEditing/index.html +11 -0
  14. package/examples/grid/cellEditing/neo-config.json +6 -0
  15. package/examples/grid/container/MainContainer.mjs +7 -6
  16. package/examples/grid/covid/GridContainer.mjs +36 -36
  17. package/examples/grid/covid/Util.mjs +1 -1
  18. package/examples/grid/covid/neo-config.json +6 -5
  19. package/examples/table/cellEditing/MainContainer.mjs +174 -0
  20. package/examples/table/cellEditing/MainContainerStateProvider.mjs +62 -0
  21. package/examples/table/cellEditing/MainModel.mjs +30 -0
  22. package/examples/table/cellEditing/MainStore.mjs +54 -0
  23. package/examples/table/cellEditing/app.mjs +6 -0
  24. package/examples/table/cellEditing/index.html +11 -0
  25. package/examples/table/cellEditing/neo-config.json +6 -0
  26. package/examples/table/nestedRecordFields/MainContainerStateProvider.mjs +2 -1
  27. package/package.json +8 -8
  28. package/resources/scss/src/apps/portal/home/FooterContainer.scss +11 -2
  29. package/resources/scss/src/examples/grid/covid/GridContainer.scss +1 -1
  30. package/resources/scss/src/grid/Container.scss +13 -23
  31. package/resources/scss/src/grid/View.scss +45 -19
  32. package/resources/scss/src/grid/header/Button.scss +2 -4
  33. package/resources/scss/src/grid/header/Toolbar.scss +1 -2
  34. package/resources/scss/src/grid/plugin/CellEditing.scss +11 -0
  35. package/resources/scss/src/table/plugin/CellEditing.scss +11 -0
  36. package/src/DefaultConfig.mjs +2 -2
  37. package/src/Xhr.mjs +1 -1
  38. package/src/button/Base.mjs +2 -2
  39. package/src/collection/Base.mjs +5 -5
  40. package/src/component/Base.mjs +3 -3
  41. package/src/component/DateSelector.mjs +15 -0
  42. package/src/container/Base.mjs +2 -2
  43. package/src/controller/Base.mjs +3 -3
  44. package/src/dialog/Base.mjs +2 -2
  45. package/src/form/field/Base.mjs +3 -6
  46. package/src/form/field/CheckBox.mjs +2 -2
  47. package/src/form/field/ComboBox.mjs +18 -2
  48. package/src/form/field/Date.mjs +10 -4
  49. package/src/form/field/FileUpload.mjs +4 -4
  50. package/src/form/field/Hidden.mjs +2 -2
  51. package/src/form/field/Text.mjs +2 -2
  52. package/src/grid/Container.mjs +340 -43
  53. package/src/grid/View.mjs +599 -124
  54. package/src/grid/header/Button.mjs +331 -36
  55. package/src/grid/header/Toolbar.mjs +111 -4
  56. package/src/grid/plugin/CellEditing.mjs +30 -0
  57. package/src/layout/Base.mjs +3 -3
  58. package/src/list/Base.mjs +2 -2
  59. package/src/list/Circle.mjs +2 -2
  60. package/src/list/Color.mjs +2 -2
  61. package/src/list/Component.mjs +2 -2
  62. package/src/main/DomEvents.mjs +12 -3
  63. package/src/manager/Base.mjs +3 -3
  64. package/src/manager/Component.mjs +20 -11
  65. package/src/manager/DomEvent.mjs +5 -6
  66. package/src/manager/Focus.mjs +2 -2
  67. package/src/manager/Instance.mjs +4 -4
  68. package/src/manager/Task.mjs +2 -2
  69. package/src/manager/Toast.mjs +3 -3
  70. package/src/plugin/Base.mjs +18 -4
  71. package/src/plugin/Popover.mjs +3 -3
  72. package/src/plugin/PrefixField.mjs +2 -2
  73. package/src/plugin/Resizable.mjs +3 -7
  74. package/src/plugin/Responsive.mjs +2 -2
  75. package/src/selection/Model.mjs +17 -2
  76. package/src/selection/grid/CellColumnModel.mjs +1 -1
  77. package/src/selection/grid/CellColumnRowModel.mjs +1 -1
  78. package/src/selection/grid/CellModel.mjs +1 -1
  79. package/src/selection/grid/ColumnModel.mjs +2 -2
  80. package/src/table/Container.mjs +32 -3
  81. package/src/table/View.mjs +9 -4
  82. package/src/table/header/Toolbar.mjs +15 -1
  83. package/src/table/plugin/CellEditing.mjs +330 -0
  84. package/src/toolbar/Base.mjs +2 -2
  85. package/src/toolbar/Breadcrumb.mjs +1 -1
  86. package/src/tooltip/Base.mjs +2 -2
  87. package/src/util/KeyNavigation.mjs +14 -8
  88. package/src/worker/Base.mjs +3 -3
  89. package/src/grid/README.md +0 -3
package/src/grid/View.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import Component from '../component/Base.mjs';
2
+ import NeoArray from '../util/Array.mjs';
2
3
  import VDomUtil from '../util/VDom.mjs';
3
4
 
4
5
  /**
5
6
  * @class Neo.grid.View
6
7
  * @extends Neo.component.Base
7
8
  */
8
- class View extends Component {
9
+ class GridView extends Component {
9
10
  static config = {
10
11
  /**
11
12
  * @member {String} className='Neo.grid.View'
@@ -17,10 +18,127 @@ class View extends Component {
17
18
  * @protected
18
19
  */
19
20
  ntype: 'grid-view',
21
+ /**
22
+ * Internal flag. Gets calculated when mounting the grid.Container
23
+ * @member {Number} availableHeight_=0
24
+ */
25
+ availableHeight_: 0,
26
+ /**
27
+ * Internal flag. Gets calculated when changing the availableHeight config
28
+ * @member {Number} availableRows_=0
29
+ */
30
+ availableRows_: 0,
31
+ /**
32
+ * Internal flag. Gets calculated after mounting grid.View rows
33
+ * @member {Number} availableWidth_=0
34
+ */
35
+ availableWidth_: 0,
20
36
  /**
21
37
  * @member {String[]} baseCls=['neo-grid-view']
38
+ * @protected
39
+ */
40
+ baseCls: ['neo-grid-view'],
41
+ /**
42
+ * The amount of rows to paint before the first & after the last visible row,
43
+ * to enhance the scrolling performance
44
+ * @member {Number} bufferRowRange_=3
45
+ */
46
+ bufferRowRange_: 3,
47
+ /**
48
+ * Define which model field contains the value of colspan definitions
49
+ * @member {String} colspanField='colspan'
50
+ */
51
+ colspanField: 'colspan',
52
+ /**
53
+ * @member {String|null} containerId=null
54
+ * @protected
55
+ */
56
+ containerId: null,
57
+ /**
58
+ * @member {Object[]} columnPositions_=[]
59
+ * @protected
60
+ */
61
+ columnPositions_: [],
62
+ /**
63
+ * @member {Boolean} isScrolling_=false
64
+ */
65
+ isScrolling_: false,
66
+ /**
67
+ * @member {Object} recordVnodeMap={}
68
+ */
69
+ recordVnodeMap: {},
70
+ /**
71
+ * @member {String} role='rowgroup'
72
+ */
73
+ role: 'rowgroup',
74
+ /**
75
+ * Number in px
76
+ * @member {Number} rowHeight_=0
77
+ */
78
+ rowHeight_: 0,
79
+ /**
80
+ * @member {Object} scrollPosition_={x:0,y:0}
22
81
  */
23
- baseCls: ['neo-grid-view']
82
+ scrollPosition_: {x: 0, y: 0},
83
+ /**
84
+ * @member {String} selectedRecordField='annotations.selected'
85
+ */
86
+ selectedRecordField: 'annotations.selected',
87
+ /**
88
+ * @member {Number} startIndex_=0
89
+ */
90
+ startIndex_: 0,
91
+ /**
92
+ * @member {Neo.data.Store|null} store_=null
93
+ */
94
+ store_: null,
95
+ /**
96
+ * @member {Boolean} useRowRecordIds=true
97
+ */
98
+ useRowRecordIds: true,
99
+ /**
100
+ * Stores the indexes of the first & last painted columns
101
+ * @member {Array} visibleColumns_=[0,0]
102
+ * @protected
103
+ */
104
+ visibleColumns_: [0, 0],
105
+ /**
106
+ * @member {String[]} wrapperCls=[]
107
+ */
108
+ wrapperCls: ['neo-grid-view-wrapper'],
109
+ /**
110
+ * @member {Object} _vdom
111
+ */
112
+ _vdom:
113
+ {cn: [
114
+ {cn: []},
115
+ {cls: 'neo-grid-scrollbar'}
116
+ ]}
117
+ }
118
+
119
+ /**
120
+ * @member {Number|null}} scrollTimeoutId=null
121
+ */
122
+ scrollTimeoutId = null
123
+
124
+ /**
125
+ * @member {Neo.grid.Container|null} gridContainer
126
+ */
127
+ get gridContainer() {
128
+ return Neo.getComponent(this.containerId)
129
+ }
130
+
131
+ /**
132
+ * @member {String[]} selectedRows
133
+ */
134
+ get selectedRows() {
135
+ let {gridContainer} = this;
136
+
137
+ if (gridContainer.selectionModel.ntype === 'selection-grid-rowmodel') {
138
+ return gridContainer.selectionModel.items
139
+ }
140
+
141
+ return []
24
142
  }
25
143
 
26
144
  /**
@@ -32,6 +150,9 @@ class View extends Component {
32
150
  let me = this;
33
151
 
34
152
  me.addDomListeners([{
153
+ scroll: me.onScroll,
154
+ scope : me
155
+ }, {
35
156
  click : me.onCellClick,
36
157
  dblclick: me.onCellDoubleClick,
37
158
  delegate: '.neo-grid-cell',
@@ -45,149 +166,369 @@ class View extends Component {
45
166
  }
46
167
 
47
168
  /**
48
- * @param {Array} inputData
169
+ * Triggered after the availableHeight config got changed
170
+ * @param {Number} value
171
+ * @param {Number} oldValue
172
+ * @protected
173
+ */
174
+ afterSetAvailableHeight(value, oldValue) {
175
+ if (value > 0) {
176
+ this.availableRows = Math.ceil(value / this.rowHeight) + 1
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Triggered after the availableRows config got changed
182
+ * @param {Number} value
183
+ * @param {Number} oldValue
184
+ * @protected
49
185
  */
50
- createViewData(inputData) {
51
- let me = this,
52
- amountRows = inputData.length,
53
- container = me.parent,
54
- columns = container.items[0].items,
55
- colCount = columns.length,
56
- data = [],
57
- i = 0,
58
- {vdom} = me,
59
- cellCls, cellStyle, config, column, dockLeftMargin, dockRightMargin, id, index, j, rendererOutput,
60
- record, rendererValue, selectedRows, trCls;
186
+ afterSetAvailableRows(value, oldValue) {
187
+ if (value > 0 && this.store.getCount() > 0) {
188
+ this.createViewData()
189
+ }
190
+ }
61
191
 
62
- me.recordVnodeMap = {}; // remove old data
192
+ /**
193
+ * Triggered after the availableWidth config got changed
194
+ * @param {Number} value
195
+ * @param {Number} oldValue
196
+ * @protected
197
+ */
198
+ afterSetAvailableWidth(value, oldValue) {
199
+ if (value > 0) {
200
+ let me = this;
63
201
 
64
- if (container.selectionModel?.ntype === 'selection-grid-rowmodel') {
65
- selectedRows = container.selectionModel.items || []
202
+ me.vdom.width = value + 'px';
203
+ me.vdom.cn[1].width = value + 'px';
204
+ me.update()
66
205
  }
206
+ }
67
207
 
68
- for (; i < amountRows; i++) {
69
- record = inputData[i];
70
- id = me.getRowId(record, i);
208
+ /**
209
+ * Triggered after the bufferRowRange config got changed
210
+ * @param {Number} value
211
+ * @param {Number} oldValue
212
+ * @protected
213
+ */
214
+ afterSetBufferRowRange(value, oldValue) {
215
+ oldValue !== undefined && this.createViewData()
216
+ }
71
217
 
72
- me.recordVnodeMap[id] = i;
218
+ /**
219
+ * Triggered after the columnPositions config got changed
220
+ * @param {Object[]} value
221
+ * @param {Object[]} oldValue
222
+ * @protected
223
+ */
224
+ afterSetColumnPositions(value, oldValue) {
225
+ let me = this;
73
226
 
74
- trCls = me.getTrClass(record, i);
227
+ if (value.length > 0) {
228
+ // for changing an array inline, we need to use the leading underscore
229
+ me._visibleColumns[1] = value.length - 1;
75
230
 
76
- if (selectedRows?.includes(id)) {
77
- trCls.push('neo-selected');
231
+ if (me.store.getCount() > 0) {
232
+ me.createViewData()
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Triggered after the id config got changed
239
+ * @param {String} value
240
+ * @param {String} oldValue
241
+ * @protected
242
+ */
243
+ afterSetId(value, oldValue) {
244
+ this.vdom.id = value + '__wrapper';
245
+
246
+ // silent vdom update, the super call will trigger the engine
247
+ super.afterSetId(value, oldValue);
248
+ }
78
249
 
79
- Neo.getComponent(me.containerId).fire('select', {
80
- record
81
- })
250
+ /**
251
+ * Triggered after the isScrolling config got changed
252
+ * @param {Number} value
253
+ * @param {Number} oldValue
254
+ * @protected
255
+ */
256
+ afterSetIsScrolling(value, oldValue) {
257
+ this.toggleCls('neo-is-scrolling', value)
258
+ }
259
+
260
+ /**
261
+ * Triggered after the rowHeight config got changed
262
+ * @param {Number} value
263
+ * @param {Number} oldValue
264
+ * @protected
265
+ */
266
+ afterSetRowHeight(value, oldValue) {
267
+ value > 0 && this.updateScrollHeight()
268
+ }
269
+
270
+ /**
271
+ * Triggered after the scrollPosition config got changed
272
+ * @param {Object} value
273
+ * @param {Object} oldValue
274
+ * @protected
275
+ */
276
+ afterSetScrollPosition(value, oldValue) {
277
+ let me = this,
278
+ {bufferRowRange} = me,
279
+ newStartIndex;
280
+
281
+ if (value.x !== oldValue?.x && me.columnPositions.length > 0) {
282
+ me.updateVisibleColumns()
283
+ }
284
+
285
+ if (value.y !== oldValue?.y) {
286
+ newStartIndex = Math.floor(value.y / me.rowHeight);
287
+
288
+ if (newStartIndex < bufferRowRange) {
289
+ me.startIndex = 0
290
+ } else if (Math.abs(me.startIndex - newStartIndex) >= bufferRowRange) {
291
+ me.startIndex = newStartIndex
82
292
  }
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Triggered after the startIndex config got changed
298
+ * @param {Number} value
299
+ * @param {Number} oldValue
300
+ * @protected
301
+ */
302
+ afterSetStartIndex(value, oldValue) {
303
+ oldValue !== undefined && this.createViewData()
304
+ }
83
305
 
84
- data.push({
85
- id,
86
- cls : trCls,
87
- cn : [],
88
- tabIndex: '-1'
306
+ /**
307
+ * Triggered after the store config got changed
308
+ * @param {Neo.data.Store|null} value
309
+ * @param {Neo.data.Store|null} oldValue
310
+ * @protected
311
+ */
312
+ afterSetStore(value, oldValue) {
313
+ if (value) {
314
+ let me = this;
315
+
316
+ value.on({
317
+ load : me.updateScrollHeight,
318
+ scope: me
89
319
  });
90
320
 
91
- dockLeftMargin = 0;
92
- dockRightMargin = 0;
321
+ value.getCount() > 0 && me.updateScrollHeight()
322
+ }
323
+ }
93
324
 
94
- j = 0;
325
+ /**
326
+ * Triggered after the visibleColumns config got changed
327
+ * @param {Number} value
328
+ * @param {Number} oldValue
329
+ * @protected
330
+ */
331
+ afterSetVisibleColumns(value, oldValue) {
332
+ if (oldValue !== undefined) {
333
+ this.createViewData()
334
+ }
335
+ }
336
+
337
+ /**
338
+ * @param {Object} data
339
+ * @param {String} [data.cellId]
340
+ * @param {Object} data.column
341
+ * @param {Neo.grid.Container} data.gridContainer
342
+ * @param {Number} data.index
343
+ * @param {Object} data.record
344
+ * @returns {Object}
345
+ */
346
+ applyRendererOutput(data) {
347
+ let {cellId, column, gridContainer, index, record} = data,
348
+ me = this,
349
+ cellCls = ['neo-grid-cell'],
350
+ colspan = record[me.colspanField],
351
+ {dataField} = column,
352
+ fieldValue = Neo.ns(dataField, false, record),
353
+ cellConfig, rendererOutput;
354
+
355
+ if (fieldValue === null || fieldValue === undefined) {
356
+ fieldValue = ''
357
+ }
95
358
 
96
- for (; j < colCount; j++) {
97
- column = columns[j];
98
- rendererValue = Neo.ns(column.field, false, record);
359
+ rendererOutput = column.renderer.call(column.rendererScope || gridContainer, {
360
+ column,
361
+ dataField,
362
+ gridContainer,
363
+ index,
364
+ record,
365
+ value: fieldValue
366
+ });
99
367
 
100
- if (rendererValue === null || rendererValue === undefined) {
101
- rendererValue = ''
368
+ switch (Neo.typeOf(rendererOutput)) {
369
+ case 'Object': {
370
+ if (rendererOutput.html) {
371
+ rendererOutput.cls && cellCls.push(...rendererOutput.cls);
372
+ } else {
373
+ rendererOutput = [rendererOutput];
102
374
  }
375
+ break
376
+ }
377
+ case 'Date':
378
+ case 'Number':
379
+ case 'String': {
380
+ rendererOutput = {
381
+ cls : cellCls,
382
+ html: rendererOutput?.toString()
383
+ };
384
+ break
385
+ }
386
+ }
103
387
 
104
- rendererOutput = column.renderer.call(column.rendererScope || container, {
105
- column,
106
- field: column.field,
107
- index: i,
108
- record,
109
- value: rendererValue
110
- });
388
+ if (rendererOutput === null || rendererOutput === undefined) {
389
+ rendererOutput = ''
390
+ }
111
391
 
112
- cellCls = rendererOutput?.cls || ['neo-grid-cell'];
392
+ if (column.cellAlign !== 'left') {
393
+ cellCls.push('neo-' + column.cellAlign)
394
+ }
113
395
 
114
- if (column.align !== 'left') {
115
- cellCls.push('neo-' + column.align)
116
- }
396
+ if (!cellId) {
397
+ cellId = me.getCellId(record, column.dataField)
398
+ }
117
399
 
118
- if (!Neo.isObject(rendererOutput)) {
119
- rendererOutput = {
120
- cls : cellCls,
121
- html: rendererOutput?.toString()
122
- }
123
- }
400
+ cellConfig = {
401
+ 'aria-colindex': index + 1, // 1 based
402
+ id : cellId,
403
+ cls : cellCls,
404
+ role : 'gridcell',
405
+ style : rendererOutput.style || {},
406
+ tabIndex : '-1'
407
+ };
408
+
409
+ if (column.width) {
410
+ cellConfig.style.minWidth = `${column.width}px`
411
+ }
124
412
 
125
- cellStyle = rendererOutput.style || {};
413
+ if (colspan && Object.keys(colspan).includes(dataField)) {
414
+ cellConfig.colspan = colspan[dataField]
415
+ }
126
416
 
127
- if (column.width) {
128
- cellStyle.minWidth = `${column.width}px`
129
- }
417
+ if (Neo.typeOf(rendererOutput) === 'Object') {
418
+ cellConfig.innerHTML = rendererOutput.html || ''
419
+ } else {
420
+ cellConfig.cn = rendererOutput
421
+ }
130
422
 
131
- config = {
132
- id : me.getCellId(record, column.field),
133
- cls : cellCls,
134
- innerHTML: rendererOutput.html || '',
135
- style : cellStyle,
136
- tabIndex : '-1'
137
- };
423
+ return cellConfig
424
+ }
138
425
 
139
- if (column.dock) {
140
- config.cls = ['neo-locked', ...config.cls || []];
426
+ /**
427
+ * @param {Object} opts
428
+ * @param {Object} opts.record
429
+ * @param {Number} [opts.rowIndex]
430
+ * @returns {Object}
431
+ */
432
+ createRow({record, rowIndex}) {
433
+ if (!Neo.isNumber(rowIndex)) {
434
+ rowIndex = this.store.indexOf(record)
435
+ }
141
436
 
142
- if (column.dock === 'left') {
143
- config.style.left = dockLeftMargin + 'px';
144
- dockLeftMargin += column.width
145
- }
146
- }
437
+ let me = this,
438
+ {gridContainer, selectedRows, visibleColumns} = me,
439
+ columns = gridContainer.items[0].items,
440
+ id = me.getRowId(record, rowIndex),
441
+ trCls = me.getTrClass(record, rowIndex),
442
+ config, column, gridRow, i;
147
443
 
148
- if (column.flex) {
149
- config.style.width = '100%'
150
- }
444
+ me.recordVnodeMap[id] = rowIndex;
445
+
446
+ if (rowIndex % 2 !== 0) {
447
+ trCls.push('neo-even')
448
+ }
449
+
450
+ if (selectedRows && Neo.ns(me.selectedRecordField, false, record)) {
451
+ NeoArray.add(selectedRows, id)
452
+ }
453
+
454
+ if (selectedRows?.includes(id)) {
455
+ trCls.push('neo-selected');
151
456
 
152
- data[i].cn.push(config)
457
+ gridContainer.fire('select', {
458
+ record
459
+ })
460
+ }
461
+
462
+ gridRow = {
463
+ id,
464
+ 'aria-rowindex': rowIndex + 2, // header row => 1, first body row => 2
465
+ cls : trCls,
466
+ cn : [],
467
+ role : 'row',
468
+ tabIndex : '-1',
469
+
470
+ style: {
471
+ height : me.rowHeight + 'px',
472
+ transform: `translate(0px, ${rowIndex * me.rowHeight}px)`
153
473
  }
474
+ };
154
475
 
155
- j = 0;
476
+ for (i=visibleColumns[0]; i <= visibleColumns[1]; i++) {
477
+ column = columns[i];
478
+ config = me.applyRendererOutput({column, gridContainer, index: rowIndex, record});
156
479
 
157
- for (; j < colCount; j++) {
158
- index = colCount - j -1;
159
- column = columns[index];
480
+ if (column.dock) {
481
+ config.cls = ['neo-locked', ...config.cls || []]
482
+ }
160
483
 
161
- if (column.dock === 'right') {
162
- data[i].cn[index].style.right = dockRightMargin + 'px';
163
- dockRightMargin += (column.width + 1) // todo: borders fix
164
- }
484
+ config.style = {
485
+ ...config.style,
486
+ left : me.columnPositions[i].x + 'px',
487
+ width: me.columnPositions[i].width + 'px'
165
488
  }
489
+
490
+ gridRow.cn.push(config)
166
491
  }
167
492
 
168
- vdom.cn = data;
493
+ return gridRow
494
+ }
495
+
496
+ /**
497
+ *
498
+ */
499
+ createViewData() {
500
+ let me = this,
501
+ {bufferRowRange, selectedRows, startIndex} = me,
502
+ rows = [],
503
+ endIndex, i;
504
+
505
+ if (me.availableRows < 1 || me.columnPositions.length < 1) {
506
+ return
507
+ }
508
+
509
+ endIndex = Math.min(me.store.getCount(), me.availableRows + startIndex + bufferRowRange);
510
+ startIndex = Math.max(0, startIndex - bufferRowRange);
511
+
512
+ for (i=startIndex; i < endIndex; i++) {
513
+ rows.push(me.createRow({record: me.store.items[i], rowIndex: i}))
514
+ }
169
515
 
170
- container.dockLeftMargin = dockLeftMargin;
171
- container.dockRightMargin = dockRightMargin;
516
+ me.getVdomRoot().cn = rows;
172
517
 
173
518
  me.promiseUpdate().then(() => {
174
519
  if (selectedRows?.length > 0) {
175
- // this logic only works for selection.table.RowModel
176
- Neo.main.DomAccess.scrollToTableRow({
177
- appName: me.appName,
178
- id : selectedRows[0]
179
- })
520
+ // this logic only works for selection.grid.RowModel
521
+ Neo.main.DomAccess.scrollToTableRow({appName: me.appName, id: selectedRows[0]})
180
522
  }
181
523
  })
182
524
  }
183
525
 
184
526
  /**
185
- * @param {Boolean} updateParentVdom
186
- * @param {Boolean} silent
527
+ * @param args
187
528
  */
188
- destroy(updateParentVdom, silent) {
529
+ destroy(...args) {
189
530
  this.store = null;
190
- super.destroy(updateParentVdom, silent)
531
+ super.destroy(...args)
191
532
  }
192
533
 
193
534
  /**
@@ -196,11 +537,11 @@ class View extends Component {
196
537
  */
197
538
  fireCellEvent(data, eventName) {
198
539
  let me = this,
199
- {id} = data.target,
540
+ id = data.currentTarget,
200
541
  dataField = me.getCellDataField(id),
201
542
  record = me.getRecord(id);
202
543
 
203
- me.parent.fire(eventName, {id: me, data, dataField, record})
544
+ me.gridContainer.fire(eventName, {data, dataField, record, view: me})
204
545
  }
205
546
 
206
547
  /**
@@ -209,10 +550,10 @@ class View extends Component {
209
550
  */
210
551
  fireRowEvent(data, eventName) {
211
552
  let me = this,
212
- {id} = data.target,
553
+ id = data.currentTarget,
213
554
  record = me.getRecord(id);
214
555
 
215
- me.parent.fire(eventName, {id: me, data, record})
556
+ me.gridContainer.fire(eventName, {data, record, view: me})
216
557
  }
217
558
 
218
559
  /**
@@ -225,15 +566,39 @@ class View extends Component {
225
566
 
226
567
  /**
227
568
  * @param {Object} record
228
- * @param {String} field
569
+ * @param {String} dataField
229
570
  * @returns {String}
230
571
  */
231
- getCellId(record, field) {
232
- return this.id + '__' + record[this.store.keyProperty] + '__' + field
572
+ getCellId(record, dataField) {
573
+ return this.id + '__' + record[this.store.keyProperty] + '__' + dataField
574
+ }
575
+
576
+ /**
577
+ * Get a grid column or column index by a given field name
578
+ * @param {String} field
579
+ * @param {Boolean} returnIndex=false
580
+ * @returns {Object|Number|null}
581
+ */
582
+ getColumn(field, returnIndex=false) {
583
+ let {gridContainer} = this,
584
+ columns = gridContainer.headerToolbar.items,
585
+ i = 0,
586
+ len = columns.length,
587
+ column;
588
+
589
+ for (; i < len; i++) {
590
+ column = columns[i];
591
+
592
+ if (column.dataField === field) {
593
+ return returnIndex ? i : column
594
+ }
595
+ }
596
+
597
+ return null
233
598
  }
234
599
 
235
600
  /**
236
- * Get the matching record by passing a row id, a cell id or an id inside a table cell.
601
+ * Get the matching record by passing a row id, a cell id or an id inside a grid cell.
237
602
  * @param {String} nodeId
238
603
  * @returns {Object|null}
239
604
  */
@@ -243,7 +608,7 @@ class View extends Component {
243
608
  node, parentNodes;
244
609
 
245
610
  if (record) {
246
- return record
611
+ return record;
247
612
  }
248
613
 
249
614
  parentNodes = VDomUtil.getParentNodes(me.vdom, nodeId);
@@ -273,7 +638,15 @@ class View extends Component {
273
638
  * @returns {String}
274
639
  */
275
640
  getRowId(record, index) {
276
- return `${this.id}__tr__${record[this.store.keyProperty]}`
641
+ let me = this,
642
+ {store} = me;
643
+
644
+ if (me.useRowRecordIds) {
645
+ return `${me.id}__tr__${record[store.keyProperty]}`
646
+ } else {
647
+ index = Neo.isNumber(index) ? index : store.indexOf(record);
648
+ return me.vdom.cn[index]?.id || Neo.getId('tr')
649
+ }
277
650
  }
278
651
 
279
652
  /**
@@ -286,6 +659,29 @@ class View extends Component {
286
659
  return ['neo-grid-row']
287
660
  }
288
661
 
662
+ /**
663
+ * @override
664
+ * @returns {*}
665
+ */
666
+ getVdomRoot() {
667
+ return this.vdom.cn[0]
668
+ }
669
+
670
+ /**
671
+ * @returns {Object[]} The new vdom items root
672
+ */
673
+ getVdomItemsRoot() {
674
+ return this.vdom.cn[0]
675
+ }
676
+
677
+ /**
678
+ * @override
679
+ * @returns {Neo.vdom.VNode}
680
+ */
681
+ getVnodeRoot() {
682
+ return this.vnode.childNodes[0]
683
+ }
684
+
289
685
  /**
290
686
  * @param {Object} data
291
687
  */
@@ -314,6 +710,25 @@ class View extends Component {
314
710
  this.fireRowEvent(data, 'rowDoubleClick')
315
711
  }
316
712
 
713
+ /**
714
+ * Only triggers for vertical scrolling
715
+ * @param {Object} data
716
+ */
717
+ onScroll(data) {
718
+ let me = this;
719
+
720
+ me.scrollTimeoutId && clearTimeout(me.scrollTimeoutId);
721
+
722
+ me.scrollTimeoutId = setTimeout(() => {
723
+ me.isScrolling = false
724
+ }, 30);
725
+
726
+ me.set({
727
+ isScrolling : true,
728
+ scrollPosition: {x: me.scrollPosition.x, y: data.scrollTop}
729
+ })
730
+ }
731
+
317
732
  /**
318
733
  * Gets triggered after changing the value of a record field.
319
734
  * E.g. myRecord.foo = 'bar';
@@ -322,25 +737,85 @@ class View extends Component {
322
737
  * @param {Neo.data.Model} opts.model The model instance of the changed record
323
738
  * @param {Object} opts.record
324
739
  */
325
- onStoreRecordChange(opts) {
326
- let me = this,
327
- deltas = [],
328
- cellId, cellNode;
740
+ onStoreRecordChange({fields, model, record}) {
741
+ let me = this,
742
+ fieldNames = fields.map(field => field.name),
743
+ needsUpdate = false,
744
+ {gridContainer} = me,
745
+ {selectionModel} = gridContainer,
746
+ {vdom} = me,
747
+ cellId, cellNode, column, index, scope;
748
+
749
+ if (fieldNames.includes(me.colspanField)) {
750
+ index = me.store.indexOf(record);
751
+ me.vdom.cn[index] = me.createRow({record, rowIndex: index});
752
+ me.update()
753
+ } else {
754
+ fields.forEach(field => {
755
+ if (field.name === me.selectedRecordField) {
756
+ if (selectionModel.ntype === 'selection-grid-rowmodel') {
757
+ selectionModel[field.value ? 'select' : 'deselect'](me.getRowId(record))
758
+ }
759
+ } else {
760
+ cellId = me.getCellId(record, field.name);
761
+ cellNode = VDomUtil.find(vdom, cellId);
762
+
763
+ // the vdom might not exist yet => nothing to do in this case
764
+ if (cellNode?.vdom) {
765
+ column = me.getColumn(field.name);
766
+ index = cellNode.index;
767
+ needsUpdate = true;
768
+ scope = column.rendererScope || gridContainer;
769
+
770
+ cellNode.parentNode.cn[index] = me.applyRendererOutput({cellId, column, gridContainer, index, record})
771
+ }
772
+ }
773
+ })
774
+ }
329
775
 
330
- opts.fields.forEach(field => {
331
- cellId = me.getCellId(opts.record, field.name);
332
- cellNode = me.getVdomChild(cellId);
776
+ needsUpdate && me.update()
777
+ }
333
778
 
334
- cellNode.innerHTML = field.value; // keep the vdom in sync
779
+ /**
780
+ *
781
+ */
782
+ updateScrollHeight() {
783
+ let me = this,
784
+ countRecords = me.store.getCount(),
785
+ {rowHeight} = me;
786
+
787
+ if (countRecords > 0 && rowHeight > 0) {
788
+ me.vdom.cn[1].height = `${(countRecords + 1) * rowHeight}px`;
789
+ me.update()
790
+ }
791
+ }
335
792
 
336
- deltas.push({
337
- id : cellId,
338
- innerHTML: field.value
339
- })
340
- });
793
+ /**
794
+ *
795
+ */
796
+ updateVisibleColumns() {
797
+ let me = this,
798
+ {x} = me.scrollPosition,
799
+ i = 0,
800
+ len = me.columnPositions.length,
801
+ endIndex = len - 1,
802
+ column, startIndex;
803
+
804
+ for (; i < len; i++) {
805
+ column = me.columnPositions[i];
806
+
807
+ if (x >= column.x && x <= column.x + column.width) {
808
+ startIndex = i
809
+ }
810
+
811
+ if (me.containerWidth + x < column.x) {
812
+ endIndex = i - 1;
813
+ break
814
+ }
815
+ }
341
816
 
342
- deltas.length > 0 && Neo.applyDeltas(me.appName, deltas)
817
+ me.visibleColumns = [startIndex, endIndex]
343
818
  }
344
819
  }
345
820
 
346
- export default Neo.setupClass(View);
821
+ export default Neo.setupClass(GridView);