neo.mjs 8.12.0 → 8.14.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 (45) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/colors/view/TableContainer.mjs +1 -1
  3. package/apps/covid/Util.mjs +9 -9
  4. package/apps/portal/index.html +1 -1
  5. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  6. package/apps/realworld2/view/article/PreviewList.mjs +2 -2
  7. package/apps/sharedcovid/Util.mjs +2 -2
  8. package/examples/ServiceWorker.mjs +2 -2
  9. package/examples/grid/covid/Util.mjs +3 -3
  10. package/examples/grid/nestedRecordFields/Viewport.mjs +27 -4
  11. package/examples/table/container/MainContainer.mjs +4 -4
  12. package/examples/table/covid/Util.mjs +3 -3
  13. package/examples/table/nestedRecordFields/Viewport.mjs +2 -2
  14. package/package.json +2 -2
  15. package/resources/scss/src/draggable/DragProxyComponent.scss +4 -1
  16. package/resources/scss/src/draggable/grid/header/toolbar/SortZone.scss +25 -2
  17. package/src/DefaultConfig.mjs +2 -2
  18. package/src/Neo.mjs +1 -1
  19. package/src/calendar/view/YearComponent.mjs +2 -2
  20. package/src/calendar/view/month/Component.mjs +1 -1
  21. package/src/calendar/view/week/EventDragZone.mjs +32 -29
  22. package/src/calendar/view/week/plugin/DragDrop.mjs +21 -21
  23. package/src/component/Base.mjs +29 -61
  24. package/src/component/DateSelector.mjs +1 -1
  25. package/src/component/Gallery.mjs +1 -1
  26. package/src/component/Helix.mjs +1 -1
  27. package/src/data/Model.mjs +0 -8
  28. package/src/draggable/DragZone.mjs +11 -5
  29. package/src/draggable/grid/header/toolbar/SortZone.mjs +111 -0
  30. package/src/draggable/list/DragZone.mjs +2 -2
  31. package/src/draggable/tab/header/toolbar/SortZone.mjs +2 -2
  32. package/src/draggable/toolbar/DragZone.mjs +3 -3
  33. package/src/draggable/toolbar/SortZone.mjs +2 -2
  34. package/src/draggable/tree/DragZone.mjs +2 -2
  35. package/src/grid/Container.mjs +2 -18
  36. package/src/grid/View.mjs +59 -44
  37. package/src/grid/header/Button.mjs +7 -6
  38. package/src/grid/header/Toolbar.mjs +4 -8
  39. package/src/main/DomAccess.mjs +1 -1
  40. package/src/main/addon/AmCharts.mjs +28 -9
  41. package/src/main/mixin/DeltaUpdates.mjs +13 -7
  42. package/src/selection/grid/RowModel.mjs +59 -20
  43. package/src/selection/table/RowModel.mjs +57 -17
  44. package/src/table/View.mjs +18 -16
  45. package/src/table/header/Button.mjs +7 -6
package/src/grid/View.mjs CHANGED
@@ -57,11 +57,6 @@ class GridView extends Component {
57
57
  * @member {String} colspanField='colspan'
58
58
  */
59
59
  colspanField: 'colspan',
60
- /**
61
- * @member {String|null} containerId=null
62
- * @protected
63
- */
64
- containerId: null,
65
60
  /**
66
61
  * Internal flag. Gets calculated after mounting grid.View rows
67
62
  * @member {Number} containerWidth_=0
@@ -139,13 +134,6 @@ class GridView extends Component {
139
134
  */
140
135
  scrollTimeoutId = null
141
136
 
142
- /**
143
- * @member {Neo.grid.Container|null} gridContainer
144
- */
145
- get gridContainer() {
146
- return Neo.getComponent(this.containerId)
147
- }
148
-
149
137
  /**
150
138
  * @member {String[]} selectedRows
151
139
  */
@@ -381,13 +369,14 @@ class GridView extends Component {
381
369
  * @param {Object} data
382
370
  * @param {String} [data.cellId]
383
371
  * @param {Object} data.column
372
+ * @param {Number} data.columnIndex
384
373
  * @param {Neo.grid.Container} data.gridContainer
385
- * @param {Number} data.index
386
374
  * @param {Object} data.record
375
+ * @param {Number} data.rowIndex
387
376
  * @returns {Object}
388
377
  */
389
378
  applyRendererOutput(data) {
390
- let {cellId, column, gridContainer, index, record} = data,
379
+ let {cellId, column, columnIndex, gridContainer, record, rowIndex} = data,
391
380
  me = this,
392
381
  cellCls = ['neo-grid-cell'],
393
382
  colspan = record[me.colspanField],
@@ -401,10 +390,11 @@ class GridView extends Component {
401
390
 
402
391
  rendererOutput = column.renderer.call(column.rendererScope || gridContainer, {
403
392
  column,
393
+ columnIndex,
404
394
  dataField,
405
395
  gridContainer,
406
- index,
407
396
  record,
397
+ rowIndex,
408
398
  value: fieldValue
409
399
  });
410
400
 
@@ -447,7 +437,7 @@ class GridView extends Component {
447
437
  }
448
438
 
449
439
  cellConfig = {
450
- 'aria-colindex': index + 1, // 1 based
440
+ 'aria-colindex': columnIndex + 1, // 1 based
451
441
  id : cellId,
452
442
  cls : cellCls,
453
443
  role : 'gridcell',
@@ -494,11 +484,12 @@ class GridView extends Component {
494
484
  rowIndex = this.store.indexOf(record)
495
485
  }
496
486
 
497
- let me = this,
498
- {bufferColumnRange, gridContainer, selectedRows, visibleColumns} = me,
499
- columns = gridContainer.items[0].items,
500
- id = me.getRowId(record, rowIndex),
501
- trCls = me.getTrClass(record, rowIndex),
487
+ let me = this,
488
+ {bufferColumnRange, selectedRows, visibleColumns} = me,
489
+ gridContainer = me.parent,
490
+ columns = gridContainer.headerToolbar.items,
491
+ id = me.getRowId(record, rowIndex),
492
+ trCls = me.getTrClass(record, rowIndex),
502
493
  config, column, endIndex, gridRow, i, startIndex;
503
494
 
504
495
  if (rowIndex % 2 !== 0) {
@@ -511,10 +502,7 @@ class GridView extends Component {
511
502
 
512
503
  if (selectedRows?.includes(id)) {
513
504
  trCls.push('neo-selected');
514
-
515
- gridContainer.fire('select', {
516
- record
517
- })
505
+ gridContainer.fire('select', {record})
518
506
  }
519
507
 
520
508
  gridRow = {
@@ -535,7 +523,7 @@ class GridView extends Component {
535
523
 
536
524
  for (i=startIndex; i <= endIndex; i++) {
537
525
  column = columns[i];
538
- config = me.applyRendererOutput({column, gridContainer, index: rowIndex, record});
526
+ config = me.applyRendererOutput({column, columnIndex: i, gridContainer, record, rowIndex});
539
527
 
540
528
  if (column.dock) {
541
529
  config.cls = ['neo-locked', ...config.cls || []]
@@ -603,7 +591,7 @@ class GridView extends Component {
603
591
  dataField = me.getCellDataField(id),
604
592
  record = me.getRecord(id);
605
593
 
606
- me.gridContainer.fire(eventName, {data, dataField, record, view: me})
594
+ me.parent.fire(eventName, {data, dataField, record, view: me})
607
595
  }
608
596
 
609
597
  /**
@@ -615,7 +603,7 @@ class GridView extends Component {
615
603
  id = data.currentTarget,
616
604
  record = me.getRecord(id);
617
605
 
618
- me.gridContainer.fire(eventName, {data, record, view: me})
606
+ me.parent.fire(eventName, {data, record, view: me})
619
607
  }
620
608
 
621
609
  /**
@@ -642,10 +630,9 @@ class GridView extends Component {
642
630
  * @returns {Object|Number|null}
643
631
  */
644
632
  getColumn(field, returnIndex=false) {
645
- let {gridContainer} = this,
646
- columns = gridContainer.headerToolbar.items,
647
- i = 0,
648
- len = columns.length,
633
+ let columns = this.parent.headerToolbar.items,
634
+ i = 0,
635
+ len = columns.length,
649
636
  column;
650
637
 
651
638
  for (; i < len; i++) {
@@ -659,6 +646,35 @@ class GridView extends Component {
659
646
  return null
660
647
  }
661
648
 
649
+ /**
650
+ * Get all painted column cells (visible + buffer range)
651
+ * @param {String} dataField
652
+ * @returns {Object[]}
653
+ */
654
+ getColumnCells(dataField) {
655
+ let me = this,
656
+ cells = [],
657
+ vdomRoot = me.getVdomRoot(),
658
+ firstRow = vdomRoot.cn[0],
659
+ i = 0,
660
+ len = firstRow.cn.length,
661
+ columnIndex;
662
+
663
+ // Columns might get moved via drag&drop, so let's check for the current match
664
+ for (; i < len; i++) {
665
+ if (dataField === me.getDataField(firstRow.cn[i].id)) {
666
+ columnIndex = i;
667
+ break;
668
+ }
669
+ }
670
+
671
+ vdomRoot.cn.forEach(row => {
672
+ cells.push(row.cn[columnIndex])
673
+ });
674
+
675
+ return cells
676
+ }
677
+
662
678
  /**
663
679
  * @param {String} cellId
664
680
  * @returns {String}
@@ -817,17 +833,16 @@ class GridView extends Component {
817
833
  * @param {Object} opts.record
818
834
  */
819
835
  onStoreRecordChange({fields, record}) {
820
- let me = this,
821
- fieldNames = fields.map(field => field.name),
822
- needsUpdate = false,
823
- {gridContainer} = me,
824
- {selectionModel} = gridContainer.view,
825
- {vdom} = me,
826
- cellId, cellNode, cellStyle, cellVdom, column, index;
836
+ let me = this,
837
+ fieldNames = fields.map(field => field.name),
838
+ needsUpdate = false,
839
+ gridContainer = me.parent,
840
+ rowIndex = me.store.indexOf(record),
841
+ {selectionModel, vdom} = me,
842
+ cellId, cellNode, cellStyle, cellVdom, column, columnIndex;
827
843
 
828
844
  if (fieldNames.includes(me.colspanField)) {
829
- index = me.store.indexOf(record);
830
- me.vdom.cn[index] = me.createRow({record, rowIndex: index});
845
+ me.vdom.cn[rowIndex] = me.createRow({record, rowIndex});
831
846
  me.update()
832
847
  } else {
833
848
  fields.forEach(field => {
@@ -843,8 +858,8 @@ class GridView extends Component {
843
858
  if (cellNode?.vdom) {
844
859
  cellStyle = cellNode.vdom.style;
845
860
  column = me.getColumn(field.name);
846
- index = cellNode.index;
847
- cellVdom = me.applyRendererOutput({cellId, column, gridContainer, index, record});
861
+ columnIndex = cellNode.index;
862
+ cellVdom = me.applyRendererOutput({cellId, column, columnIndex, gridContainer, record, rowIndex});
848
863
  needsUpdate = true;
849
864
 
850
865
  // The cell-positioning logic happens outside applyRendererOutput()
@@ -854,7 +869,7 @@ class GridView extends Component {
854
869
  width: cellStyle.width
855
870
  });
856
871
 
857
- cellNode.parentNode.cn[index] = cellVdom
872
+ cellNode.parentNode.cn[columnIndex] = cellVdom
858
873
  }
859
874
  }
860
875
  })
@@ -228,13 +228,14 @@ class Button extends BaseButton {
228
228
  }
229
229
 
230
230
  /**
231
- * @param {Object} data
232
- * @param {Neo.button.Base} data.column
233
- * @param {String} data.dataField
231
+ * @param {Object} data
232
+ * @param {Neo.button.Base} data.column
233
+ * @param {Number} data.columnIndex
234
+ * @param {String} data.dataField
234
235
  * @param {Neo.grid.Container} data.gridContainer
235
- * @param {Number} data.index
236
- * @param {Object} data.record
237
- * @param {Number|String} data.value
236
+ * @param {Object} data.record
237
+ * @param {Number} data.rowIndex
238
+ * @param {Number|String} data.value
238
239
  * @returns {*}
239
240
  */
240
241
  cellRenderer(data) {
@@ -1,5 +1,4 @@
1
1
  import BaseToolbar from '../../toolbar/Base.mjs';
2
- import NeoArray from '../../util/Array.mjs';
3
2
 
4
3
  /**
5
4
  * @class Neo.grid.header.Toolbar
@@ -25,10 +24,6 @@ class Toolbar extends BaseToolbar {
25
24
  * @member {Boolean} draggable_=true
26
25
  */
27
26
  draggable_: true,
28
- /**
29
- * @member {Neo.grid.Container|null} gridContainer=null
30
- */
31
- gridContainer: null,
32
27
  /**
33
28
  * @member {Object} itemDefaults={ntype: 'grid-header-button'}
34
29
  */
@@ -188,9 +183,10 @@ class Toolbar extends BaseToolbar {
188
183
  */
189
184
  async passSizeToView(silent=false) {
190
185
  let me = this,
191
- rects = await me.getDomRect(me.items.map(item => item.id)),
186
+ {items} = me,
187
+ rects = await me.getDomRect(items.map(item => item.id)),
192
188
  lastItem = rects[rects.length - 1],
193
- columnPositions = rects.map(item => ({width: item.width, x: item.x - rects[0].x})),
189
+ columnPositions = rects.map((item, index) => ({dataField: items[index].dataField, width: item.width, x: item.x - rects[0].x})),
194
190
  i = 1,
195
191
  len = columnPositions.length,
196
192
  layoutFinished = true;
@@ -208,7 +204,7 @@ class Toolbar extends BaseToolbar {
208
204
  await me.timeout(100);
209
205
  await me.passSizeToView(silent)
210
206
  } else {
211
- me.gridContainer.view[silent ? 'setSilent' : 'set']({
207
+ me.parent.view[silent ? 'setSilent' : 'set']({
212
208
  availableWidth: lastItem.x + lastItem.width - rects[0].x,
213
209
  columnPositions
214
210
  })
@@ -576,7 +576,7 @@ class DomAccess extends Base {
576
576
  * Include a script into the document.head
577
577
  * You can add more attributes if needed. See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
578
578
  * @param {String} src
579
- * @param {Object} opts=defer:true}
579
+ * @param {Object} opts={defer:true}
580
580
  * @param {Boolean} [opts.async]
581
581
  * @param {Boolean} [opts.defer]
582
582
  * @returns {Promise<unknown>}
@@ -28,15 +28,15 @@ class AmCharts extends Base {
28
28
  */
29
29
  dataMap: {},
30
30
  /**
31
- * @member {String} downloadPath='https//www.amcharts.com/lib/4/'
31
+ * @member {String} downloadPath='https//cdn.amcharts.com/lib/4/'
32
32
  * @protected
33
33
  */
34
- downloadPath: 'https://www.amcharts.com/lib/4/',
34
+ downloadPath: 'https://cdn.amcharts.com/lib/4/',
35
35
  /**
36
- * @member {String} fallbackPath='https://neomjs.github.io/pages/resources_pub/amCharts/'
36
+ * @member {String} fallbackPath='https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/amCharts'
37
37
  * @protected
38
38
  */
39
- fallbackPath: 'https://neomjs.github.io/pages/resources_pub/amCharts/',
39
+ fallbackPath: 'https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/amCharts/',
40
40
  /**
41
41
  * Remote method access for other workers
42
42
  * @member {Object} remote
@@ -51,7 +51,13 @@ class AmCharts extends Base {
51
51
  'setProperty',
52
52
  'updateData'
53
53
  ]
54
- }
54
+ },
55
+ /**
56
+ * Enforce using the fallbackPath
57
+ * @member {Boolean} useFallbackPath=false
58
+ * @protected
59
+ */
60
+ useFallbackPath: false
55
61
  }
56
62
 
57
63
  /**
@@ -187,8 +193,19 @@ class AmCharts extends Base {
187
193
  * @param {Boolean} useFallback=false
188
194
  */
189
195
  loadFiles(useFallback=false) {
190
- let me = this,
191
- basePath = useFallback ? me.fallbackPath : me.downloadPath;
196
+ let me = this,
197
+ useFallbackPath = me.useFallbackPath || useFallback,
198
+ basePath;
199
+
200
+ if (useFallbackPath && Neo.config.isGitHubPages) {
201
+ basePath = '../../../../resources_pub/amCharts/';
202
+
203
+ if (Neo.config.environment !== 'development') {
204
+ basePath = `../../${basePath}`
205
+ }
206
+ } else {
207
+ basePath = useFallbackPath ? me.fallbackPath : me.downloadPath
208
+ }
192
209
 
193
210
  me.isLoading = true;
194
211
 
@@ -203,8 +220,10 @@ class AmCharts extends Base {
203
220
  me.isReady = true
204
221
  })
205
222
  }).catch(e => {
206
- console.log('Download from amcharts.com failed, switching to fallback', e);
207
- me.loadFiles(true)
223
+ if (!useFallback && !me.useFallbackPath) {
224
+ console.log('Download from amcharts.com failed, switching to fallback', e);
225
+ me.loadFiles(true)
226
+ }
208
227
  })
209
228
  }
210
229
 
@@ -115,18 +115,24 @@ class DeltaUpdates extends Base {
115
115
  * @param {String} delta.index
116
116
  * @param {String} delta.parentId
117
117
  */
118
- du_moveNode(delta) {
119
- let {index} = delta,
120
- node = this.getElement(delta.id),
121
- parentNode = this.getElement(delta.parentId);
118
+ du_moveNode({id, index, parentId}) {
119
+ let node = this.getElement(id),
120
+ parentNode = this.getElement(parentId),
121
+ currentNode;
122
122
 
123
123
  if (node && parentNode) {
124
124
  if (index >= parentNode.children.length) {
125
125
  parentNode.appendChild(node)
126
126
  } else {
127
- //index++; // todo?: increase the index in case same parent, oldIndex < newIndex, direct swap
128
- if (node && parentNode.children[index].id !== delta.id) {
129
- parentNode.insertBefore(node, parentNode.children[index])
127
+ currentNode = parentNode.children[index];
128
+
129
+ if (node && currentNode.id !== id) {
130
+ // Check for a direct swap OP
131
+ if (node === currentNode.nextElementSibling) {
132
+ node.replaceWith(currentNode)
133
+ }
134
+
135
+ parentNode.insertBefore(node, currentNode)
130
136
  }
131
137
  }
132
138
  }
@@ -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.grid.RowModel
@@ -30,7 +29,7 @@ class RowModel extends BaseModel {
30
29
  addDomListener() {
31
30
  let me = this;
32
31
 
33
- me.view.gridContainer.on('rowClick', me.onRowClick, me)
32
+ me.view.parent.on('rowClick', me.onRowClick, me)
34
33
  }
35
34
 
36
35
  /**
@@ -39,11 +38,19 @@ class RowModel extends BaseModel {
39
38
  destroy(...args) {
40
39
  let me = this;
41
40
 
42
- me.view.gridContainer.un('rowClick', me.onRowClick, me);
41
+ me.view.parent.un('rowClick', me.onRowClick, me);
43
42
 
44
43
  super.destroy(...args)
45
44
  }
46
45
 
46
+ /**
47
+ * @param {Record} record
48
+ * @returns {Boolean}
49
+ */
50
+ hasAnnotations(record) {
51
+ return !!Object.getOwnPropertyDescriptor(record.__proto__, this.view.selectedRecordField)
52
+ }
53
+
47
54
  /**
48
55
  * @param {Object} data
49
56
  */
@@ -65,28 +72,31 @@ class RowModel extends BaseModel {
65
72
  let me = this,
66
73
  {view} = me,
67
74
  {store} = view,
75
+ countRecords = store.getCount(),
68
76
  currentIndex = 0,
69
- newIndex, newRecord, rowId;
77
+ newIndex, record, rowId;
70
78
 
71
79
  if (me.hasSelection()) {
72
80
  currentIndex = store.indexOf(view.getRecordByRowId(me.items[0]))
73
81
  }
74
82
 
75
- newIndex = (currentIndex + step) % store.getCount();
83
+ newIndex = (currentIndex + step) % countRecords;
76
84
 
77
85
  while (newIndex < 0) {
78
- newIndex += store.getCount()
86
+ newIndex += countRecords
79
87
  }
80
88
 
81
- newRecord = store.getAt(newIndex);
82
- rowId = view.getRowId(newRecord);
89
+ record = store.getAt(newIndex);
83
90
 
84
- if (rowId) {
85
- me.select(rowId);
91
+ if (me.hasAnnotations(record)) {
92
+ me.updateAnnotations(record)
93
+ } else {
94
+ rowId = view.getRowId(record);
86
95
 
87
- view.fire('select', {
88
- record: store.getAt(newIndex)
89
- })
96
+ if (rowId) {
97
+ me.select(rowId);
98
+ view.fire('select', {record})
99
+ }
90
100
  }
91
101
  }
92
102
 
@@ -100,16 +110,19 @@ class RowModel extends BaseModel {
100
110
  isSelected, record;
101
111
 
102
112
  if (id) {
103
- me.toggleSelection(id);
113
+ record = view.getRecord(id);
114
+
115
+ if (me.hasAnnotations(record)) {
116
+ me.updateAnnotations(record)
117
+ } else {
118
+ me.toggleSelection(id);
104
119
 
105
- isSelected = me.isSelected(id);
106
- record = view.getRecord(id);
120
+ isSelected = me.isSelected(id);
107
121
 
108
- !isSelected && view.onDeselect?.(record);
122
+ !isSelected && view.onDeselect?.(record);
109
123
 
110
- view.fire(isSelected ? 'select' : 'deselect', {
111
- record
112
- })
124
+ view.fire(isSelected ? 'select' : 'deselect', {record})
125
+ }
113
126
  }
114
127
  }
115
128
 
@@ -140,6 +153,32 @@ class RowModel extends BaseModel {
140
153
 
141
154
  super.unregister()
142
155
  }
156
+
157
+ /**
158
+ * @param {Record} record
159
+ */
160
+ updateAnnotations(record) {
161
+ let me = this,
162
+ {view} = me,
163
+ rowId = view.getRowId(record),
164
+ isSelected = me.isSelected(rowId),
165
+ annotationsField = view.selectedRecordField;
166
+
167
+ if (me.singleSelect) {
168
+ if (isSelected) {
169
+ record[annotationsField] = false
170
+ } else {
171
+ me.items.forEach(rowId => {
172
+ // We can use setSilent(), since the last change will trigger a view update
173
+ view.getRecordByRowId(rowId).setSilent({[annotationsField]: false})
174
+ });
175
+
176
+ record[annotationsField] = true
177
+ }
178
+ } else {
179
+ record[annotationsField] = !record[annotationsField]
180
+ }
181
+ }
143
182
  }
144
183
 
145
184
  export default Neo.setupClass(RowModel);
@@ -23,6 +23,14 @@ class RowModel extends BaseModel {
23
23
  cls: 'neo-selection-rowmodel'
24
24
  }
25
25
 
26
+ /**
27
+ * @param {Record} record
28
+ * @returns {Boolean}
29
+ */
30
+ hasAnnotations(record) {
31
+ return !!Object.getOwnPropertyDescriptor(record.__proto__, this.view.selectedRecordField)
32
+ }
33
+
26
34
  /**
27
35
  *
28
36
  */
@@ -64,28 +72,31 @@ class RowModel extends BaseModel {
64
72
  let me = this,
65
73
  {view} = me,
66
74
  {store} = view,
75
+ countRecords = store.getCount(),
67
76
  currentIndex = 0,
68
- newIndex, newRecord, rowId;
77
+ newIndex, record, rowId;
69
78
 
70
79
  if (me.hasSelection()) {
71
80
  currentIndex = store.indexOf(view.getRecordByRowId(me.items[0]))
72
81
  }
73
82
 
74
- newIndex = (currentIndex + step) % store.getCount();
83
+ newIndex = (currentIndex + step) % countRecords;
75
84
 
76
85
  while (newIndex < 0) {
77
- newIndex += store.getCount()
86
+ newIndex += countRecords
78
87
  }
79
88
 
80
- newRecord = store.getAt(newIndex);
81
- rowId = view.getRowId(newRecord);
89
+ record = store.getAt(newIndex);
82
90
 
83
- if (rowId) {
84
- me.select(rowId);
91
+ if (me.hasAnnotations(record)) {
92
+ me.updateAnnotations(record)
93
+ } else {
94
+ rowId = view.getRowId(record);
85
95
 
86
- view.fire('select', {
87
- record: store.getAt(newIndex)
88
- })
96
+ if (rowId) {
97
+ me.select(rowId);
98
+ view.fire('select', {record})
99
+ }
89
100
  }
90
101
  }
91
102
 
@@ -99,16 +110,19 @@ class RowModel extends BaseModel {
99
110
  isSelected, record;
100
111
 
101
112
  if (id) {
102
- me.toggleSelection(id);
113
+ record = view.getRecord(id);
114
+
115
+ if (me.hasAnnotations(record)) {
116
+ me.updateAnnotations(record)
117
+ } else {
118
+ me.toggleSelection(id);
103
119
 
104
- isSelected = me.isSelected(id);
105
- record = view.getRecord(id);
120
+ isSelected = me.isSelected(id);
106
121
 
107
- !isSelected && view.onDeselect?.(record);
122
+ !isSelected && view.onDeselect?.(record);
108
123
 
109
- view.fire(isSelected ? 'select' : 'deselect', {
110
- record
111
- })
124
+ view.fire(isSelected ? 'select' : 'deselect', {record})
125
+ }
112
126
  }
113
127
  }
114
128
 
@@ -139,6 +153,32 @@ class RowModel extends BaseModel {
139
153
 
140
154
  super.unregister()
141
155
  }
156
+
157
+ /**
158
+ * @param {Record} record
159
+ */
160
+ updateAnnotations(record) {
161
+ let me = this,
162
+ {view} = me,
163
+ rowId = view.getRowId(record),
164
+ isSelected = me.isSelected(rowId),
165
+ annotationsField = view.selectedRecordField;
166
+
167
+ if (me.singleSelect) {
168
+ if (isSelected) {
169
+ record[annotationsField] = false
170
+ } else {
171
+ me.items.forEach(rowId => {
172
+ // We can use setSilent(), since the last change will trigger a view update
173
+ view.getRecordByRowId(rowId).setSilent({[annotationsField]: false})
174
+ });
175
+
176
+ record[annotationsField] = true
177
+ }
178
+ } else {
179
+ record[annotationsField] = !record[annotationsField]
180
+ }
181
+ }
142
182
  }
143
183
 
144
184
  export default Neo.setupClass(RowModel);