neo.mjs 8.23.0 → 8.25.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.
@@ -0,0 +1,179 @@
1
+ import Base from '../core/Base.mjs';
2
+
3
+ /**
4
+ * @class Neo.grid.ScrollManager
5
+ * @extends Neo.core.Base
6
+ */
7
+ class ScrollManager extends Base {
8
+ static config = {
9
+ /**
10
+ * @member {String} className='Neo.grid.ScrollManager'
11
+ * @protected
12
+ */
13
+ className: 'Neo.grid.ScrollManager',
14
+ /**
15
+ * @member {Number} scrollLeft_=0
16
+ * @protected
17
+ */
18
+ scrollLeft_: 0,
19
+ /**
20
+ * @member {Number} scrollTop_=0
21
+ * @protected
22
+ */
23
+ scrollTop_: 0
24
+ }
25
+
26
+ /**
27
+ * @member {Neo.grid.Container|null} gridContainer=null
28
+ * @protected
29
+ */
30
+ gridContainer = null
31
+ /**
32
+ * @member {Neo.grid.View|null} gridView=null
33
+ * @protected
34
+ */
35
+ gridView = null
36
+ /**
37
+ * Storing touchmove position for mobile envs
38
+ * @member {Number} lastTouchX=0
39
+ * @protected
40
+ */
41
+ lastTouchX = 0
42
+ /**
43
+ * Storing touchmove position for mobile envs
44
+ * @member {Number} lastTouchY=0
45
+ * @protected
46
+ */
47
+ lastTouchY = 0
48
+ /**
49
+ * @member {Number|null}} scrollTimeoutId=null
50
+ * @protected
51
+ */
52
+ scrollTimeoutId = null
53
+ /**
54
+ * Flag for identifying the ownership of a touchmove operation
55
+ * @member {'container'|'view'|null} touchMoveOwner=null
56
+ * @protected
57
+ */
58
+ touchMoveOwner = null
59
+
60
+ /**
61
+ * @param {Object} config
62
+ */
63
+ construct(config) {
64
+ super.construct(config);
65
+
66
+ let me = this;
67
+
68
+ me.gridContainer.addDomListeners({
69
+ scroll: me.onContainerScroll,
70
+ scope : me
71
+ });
72
+
73
+ me.gridView.addDomListeners({
74
+ scroll : me.onViewScroll,
75
+ touchcancel: me.onTouchCancel,
76
+ touchend : me.onTouchEnd,
77
+ scope : me
78
+ })
79
+ }
80
+
81
+ /**
82
+ * @param {Object} data
83
+ * @param {Number} data.scrollLeft
84
+ * @param {Object} data.target
85
+ * @param {Object} data.touches
86
+ */
87
+ onContainerScroll({scrollLeft, target, touches}) {
88
+ let me = this,
89
+ view = me.gridView,
90
+ deltaY, lastTouchY;
91
+
92
+ // We must ignore events for grid-scrollbar
93
+ if (target.id.includes('grid-container')) {
94
+ me .scrollLeft = scrollLeft;
95
+ view.scrollLeft = scrollLeft;
96
+
97
+ me.gridContainer.headerToolbar.scrollLeft = scrollLeft;
98
+
99
+ if (touches) {
100
+ if (me.touchMoveOwner !== 'view') {
101
+ me.touchMoveOwner = 'container'
102
+ }
103
+
104
+ if (me.touchMoveOwner === 'container') {
105
+ lastTouchY = touches.lastTouch.clientY - touches.firstTouch.clientY;
106
+ deltaY = me.lastTouchY - lastTouchY;
107
+
108
+ deltaY !== 0 && Neo.main.DomAccess.scrollTo({
109
+ direction: 'top',
110
+ id : view.vdom.id,
111
+ value : me.scrollTop + deltaY
112
+ })
113
+
114
+ me.lastTouchY = lastTouchY
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * @param {Object} data
122
+ */
123
+ onTouchCancel(data) {
124
+ this.onTouchEnd(data)
125
+ }
126
+
127
+ /**
128
+ * @param {Object} data
129
+ */
130
+ onTouchEnd(data) {
131
+ let me = this;
132
+
133
+ me.touchMoveOwner = null;
134
+ me.lastTouchX = 0;
135
+ me.lastTouchY = 0
136
+ }
137
+
138
+ /**
139
+ * Only triggers for vertical scrolling
140
+ * @param {Object} data
141
+ * @protected
142
+ */
143
+ onViewScroll({scrollTop, touches}) {
144
+ let me = this,
145
+ view = me.gridView,
146
+ deltaX, lastTouchX;
147
+
148
+ me.scrollTop = scrollTop;
149
+
150
+ me.scrollTimeoutId && clearTimeout(me.scrollTimeoutId);
151
+
152
+ me.scrollTimeoutId = setTimeout(() => {
153
+ view.isScrolling = false
154
+ }, 30);
155
+
156
+ view.set({isScrolling: true, scrollTop});
157
+
158
+ if (touches) {
159
+ if (me.touchMoveOwner !== 'container') {
160
+ me.touchMoveOwner = 'view'
161
+ }
162
+
163
+ if (me.touchMoveOwner === 'view') {
164
+ lastTouchX = touches.lastTouch.clientX - touches.firstTouch.clientX;
165
+ deltaX = me.lastTouchX - lastTouchX;
166
+
167
+ deltaX !== 0 && Neo.main.DomAccess.scrollTo({
168
+ direction: 'left',
169
+ id : me.gridContainer.id,
170
+ value : me.scrollLeft + deltaX
171
+ })
172
+
173
+ me.lastTouchX = lastTouchX
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ export default Neo.setupClass(ScrollManager);
@@ -1,26 +1,28 @@
1
1
  import Component from '../component/Base.mjs';
2
2
 
3
3
  /**
4
- * @class Neo.grid.Scrollbar
4
+ * We do not want to use the default scrollbar for vertical scrolling, since it would show up at the right edge
5
+ * of the last column. Instead, we want to show it at the right edge of the container (always visible when scrolling).
6
+ * @class Neo.grid.VerticalScrollbar
5
7
  * @extends Neo.component.Base
6
8
  */
7
- class GridScrollbar extends Component {
9
+ class VerticalScrollbar extends Component {
8
10
  static config = {
9
11
  /**
10
- * @member {String} className='Neo.grid.Scrollbar'
12
+ * @member {String} className='Neo.grid.VerticalScrollbar'
11
13
  * @protected
12
14
  */
13
- className: 'Neo.grid.Scrollbar',
15
+ className: 'Neo.grid.VerticalScrollbar',
14
16
  /**
15
- * @member {String} ntype='grid-scrollbar'
17
+ * @member {String} ntype='grid-vertical-scrollbar'
16
18
  * @protected
17
19
  */
18
- ntype: 'grid-scrollbar',
20
+ ntype: 'grid-vertical-scrollbar',
19
21
  /**
20
- * @member {String[]} baseCls=['neo-grid-scrollbar']
22
+ * @member {String[]} baseCls=['neo-grid-vertical-scrollbar']
21
23
  * @protected
22
24
  */
23
- baseCls: ['neo-grid-scrollbar'],
25
+ baseCls: ['neo-grid-vertical-scrollbar'],
24
26
  /**
25
27
  * Number in px
26
28
  * @member {Number} rowHeight_=0
@@ -116,4 +118,4 @@ class GridScrollbar extends Component {
116
118
  }
117
119
  }
118
120
 
119
- export default Neo.setupClass(GridScrollbar);
121
+ export default Neo.setupClass(VerticalScrollbar);
package/src/grid/View.mjs CHANGED
@@ -103,9 +103,15 @@ class GridView extends Component {
103
103
  */
104
104
  rowHeight_: 0,
105
105
  /**
106
- * @member {Object} scrollPosition_={x:0,y:0}
106
+ * @member {Number} scrollLeft_=0
107
+ * @protected
107
108
  */
108
- scrollPosition_: {x: 0, y: 0},
109
+ scrollLeft_: 0,
110
+ /**
111
+ * @member {Number} scrollTop_=0
112
+ * @protected
113
+ */
114
+ scrollTop_: 0,
109
115
  /**
110
116
  * @member {Neo.selection.Model} selectionModel_=null
111
117
  */
@@ -147,23 +153,6 @@ class GridView extends Component {
147
153
  ]}
148
154
  }
149
155
 
150
- /**
151
- * Flag for identifying the ownership of a touchmove operation
152
- * @member {Boolean} isTouchMoveOwner=false
153
- * @protected
154
- */
155
- isTouchMoveOwner = false
156
- /**
157
- * Storing touchmove position for mobile envs
158
- * @member {Number} lastTouchX=0
159
- * @protected
160
- */
161
- lastTouchX = 0
162
- /**
163
- * @member {Number|null}} scrollTimeoutId=null
164
- */
165
- scrollTimeoutId = null
166
-
167
156
  /**
168
157
  * @member {String[]} selectedRows
169
158
  */
@@ -199,11 +188,6 @@ class GridView extends Component {
199
188
  let me = this;
200
189
 
201
190
  me.addDomListeners([{
202
- scroll : me.onScroll,
203
- touchcancel: me.onTouchCancel,
204
- touchend : me.onTouchEnd,
205
- scope : me
206
- }, {
207
191
  click : me.onCellClick,
208
192
  dblclick: me.onCellDoubleClick,
209
193
  delegate: '.neo-grid-cell',
@@ -314,7 +298,7 @@ class GridView extends Component {
314
298
  * @protected
315
299
  */
316
300
  afterSetMountedColumns(value, oldValue) {
317
- oldValue !== undefined && this.createViewData()
301
+ oldValue && this.createViewData()
318
302
  }
319
303
 
320
304
  /**
@@ -328,29 +312,31 @@ class GridView extends Component {
328
312
  }
329
313
 
330
314
  /**
331
- * Triggered after the scrollPosition config got changed
332
- * @param {Object} value
333
- * @param {Object} oldValue
315
+ * Triggered after the scrollLeft config got changed
316
+ * @param {Number} value
317
+ * @param {Number} oldValue
334
318
  * @protected
335
319
  */
336
- afterSetScrollPosition(value, oldValue) {
320
+ afterSetScrollLeft(value, oldValue) {
321
+ this.updateMountedAndVisibleColumns()
322
+ }
323
+
324
+ /**
325
+ * Triggered after the scrollTop config got changed
326
+ * @param {Number} value
327
+ * @param {Number} oldValue
328
+ * @protected
329
+ */
330
+ afterSetScrollTop(value, oldValue) {
337
331
  let me = this,
338
332
  {bufferRowRange} = me,
339
- newStartIndex;
333
+ newStartIndex = Math.floor(value / me.rowHeight);
340
334
 
341
- if (value.x !== oldValue?.x) {
342
- me.updateMountedAndVisibleColumns()
343
- }
344
-
345
- if (value.y !== oldValue?.y) {
346
- newStartIndex = Math.floor(value.y / me.rowHeight);
347
-
348
- if (Math.abs(me.startIndex - newStartIndex) >= bufferRowRange) {
349
- me.startIndex = newStartIndex
350
- } else {
351
- me.visibleRows[0] = newStartIndex;
352
- me.visibleRows[1] = newStartIndex + me.availableRows
353
- }
335
+ if (Math.abs(me.startIndex - newStartIndex) >= bufferRowRange) {
336
+ me.startIndex = newStartIndex
337
+ } else {
338
+ me.visibleRows[0] = newStartIndex;
339
+ me.visibleRows[1] = newStartIndex + me.availableRows
354
340
  }
355
341
  }
356
342
 
@@ -861,46 +847,6 @@ class GridView extends Component {
861
847
  this.fireRowEvent(data, 'rowDoubleClick')
862
848
  }
863
849
 
864
- /**
865
- * Only triggers for vertical scrolling
866
- * @param {Object} data
867
- * @protected
868
- */
869
- onScroll({scrollTop, touches}) {
870
- let me = this,
871
- deltaX, lastTouchX;
872
-
873
- me.scrollTimeoutId && clearTimeout(me.scrollTimeoutId);
874
-
875
- me.scrollTimeoutId = setTimeout(() => {
876
- me.isScrolling = false
877
- }, 30);
878
-
879
- me.set({
880
- isScrolling : true,
881
- scrollPosition: {x: me.scrollPosition.x, y: scrollTop}
882
- });
883
-
884
- if (touches) {
885
- if (!me.parent.isTouchMoveOwner) {
886
- me.isTouchMoveOwner = true
887
- }
888
-
889
- if (me.isTouchMoveOwner) {
890
- lastTouchX = touches.lastTouch.clientX - touches.firstTouch.clientX;
891
- deltaX = me.lastTouchX - lastTouchX;
892
-
893
- deltaX !== 0 && Neo.main.DomAccess.scrollTo({
894
- direction: 'left',
895
- id : me.parent.id,
896
- value : me.scrollPosition.x + deltaX
897
- })
898
-
899
- me.lastTouchX = lastTouchX
900
- }
901
- }
902
- }
903
-
904
850
  /**
905
851
  * Gets triggered after changing the value of a record field.
906
852
  * E.g. myRecord.foo = 'bar';
@@ -954,34 +900,6 @@ class GridView extends Component {
954
900
  needsUpdate && me.update()
955
901
  }
956
902
 
957
- /**
958
- * @param {Object} data
959
- */
960
- onTouchCancel(data) {
961
- let me = this,
962
- {parent} = me;
963
-
964
- me.isTouchMoveOwner = false;
965
- me.lastTouchX = 0;
966
-
967
- parent.isTouchMoveOwner = false;
968
- parent.lastTouchY = 0
969
- }
970
-
971
- /**
972
- * @param {Object} data
973
- */
974
- onTouchEnd(data) {
975
- let me = this,
976
- {parent} = me;
977
-
978
- me.isTouchMoveOwner = false;
979
- me.lastTouchX = 0;
980
-
981
- parent.isTouchMoveOwner = false;
982
- parent.lastTouchY = 0
983
- }
984
-
985
903
  /**
986
904
  * Used for keyboard navigation (selection models)
987
905
  * @param {Number} index
@@ -992,7 +910,7 @@ class GridView extends Component {
992
910
  {mountedRows, visibleRows} = me,
993
911
  countRecords = me.store.getCount(),
994
912
  newIndex = index + step,
995
- lastRowGap, mounted, scrollPosition, visible;
913
+ lastRowGap, mounted, scrollTop, visible;
996
914
 
997
915
  if (newIndex >= countRecords) {
998
916
  newIndex %= countRecords;
@@ -1017,15 +935,15 @@ class GridView extends Component {
1017
935
  }
1018
936
 
1019
937
  if (step < 0) {
1020
- scrollPosition = newIndex * me.rowHeight
938
+ scrollTop = newIndex * me.rowHeight
1021
939
  } else {
1022
- lastRowGap = me.rowHeight - (me.availableHeight % me.rowHeight);
1023
- scrollPosition = (newIndex - me.availableRows) * me.rowHeight + lastRowGap
940
+ lastRowGap = me.rowHeight - (me.availableHeight % me.rowHeight);
941
+ scrollTop = (newIndex - me.availableRows) * me.rowHeight + lastRowGap
1024
942
  }
1025
943
 
1026
944
  Neo.main.DomAccess.scrollTo({
1027
945
  id : me.vdom.id,
1028
- value : scrollPosition,
946
+ value : scrollTop,
1029
947
  windowId: me.windowId
1030
948
  })
1031
949
  }
@@ -1036,18 +954,18 @@ class GridView extends Component {
1036
954
  */
1037
955
  updateMountedAndVisibleColumns() {
1038
956
  let me = this,
1039
- {bufferColumnRange, columnPositions, visibleColumns} = me,
1040
- {x} = me.scrollPosition,
1041
- i = 0,
1042
- len = columnPositions.getCount(),
1043
- endIndex = len - 1,
957
+ {bufferColumnRange, columnPositions, mountedColumns, visibleColumns} = me,
958
+ i = 0,
959
+ countColumns = columnPositions.getCount(),
960
+ endIndex = countColumns - 1,
961
+ x = me.scrollLeft,
1044
962
  column, startIndex;
1045
963
 
1046
- if (len < 1) {
964
+ if (countColumns < 1) {
1047
965
  return
1048
966
  }
1049
967
 
1050
- for (; i < len; i++) {
968
+ for (; i < countColumns; i++) {
1051
969
  column = columnPositions.getAt(i);
1052
970
 
1053
971
  if (x >= column.x && x <= column.x + column.width) {
@@ -1060,15 +978,12 @@ class GridView extends Component {
1060
978
  }
1061
979
  }
1062
980
 
1063
- if (
1064
- Math.abs(startIndex - me.visibleColumns[0]) >= me.bufferColumnRange ||
1065
- me.visibleColumns[1] < 1 // initial call
1066
- ) {
1067
- visibleColumns[0] = startIndex;
1068
- visibleColumns[1] = endIndex;
981
+ visibleColumns[0] = startIndex; // update the array inline
982
+ visibleColumns[1] = endIndex;
1069
983
 
1070
- endIndex = Math.min(len - 1, visibleColumns[1] + bufferColumnRange);
984
+ if (visibleColumns[0] <= mountedColumns[0] || visibleColumns[1] >= mountedColumns[1]) {
1071
985
  startIndex = Math.max(0, visibleColumns[0] - bufferColumnRange);
986
+ endIndex = Math.min(countColumns - 1, visibleColumns[1] + bufferColumnRange);
1072
987
 
1073
988
  me.mountedColumns = [startIndex, endIndex]
1074
989
  }
@@ -235,10 +235,9 @@ class Toolbar extends BaseToolbar {
235
235
 
236
236
  /**
237
237
  * @param {Number} index
238
- * @param {DOMRect} itemRect
239
238
  * @returns {Promise<void>}
240
239
  */
241
- async scrollToIndex(index, itemRect) {
240
+ async scrollToIndex(index) {
242
241
  await Neo.main.DomAccess.scrollIntoView({
243
242
  delay : 125,
244
243
  id : this.items[index].id,
@@ -85,7 +85,7 @@ class CellModel extends BaseModel {
85
85
  let me = this,
86
86
  {dataFields, view} = me,
87
87
  {store} = view,
88
- currentColumn, newIndex, record;
88
+ currentColumn, currentIndex, newIndex, record;
89
89
 
90
90
  if (me.hasSelection()) {
91
91
  currentColumn = view.getDataField(me.items[0]);
@@ -95,13 +95,16 @@ class CellModel extends BaseModel {
95
95
  record = store.getAt(0)
96
96
  }
97
97
 
98
- newIndex = (dataFields.indexOf(currentColumn) + step) % dataFields.length;
98
+ currentIndex = dataFields.indexOf(currentColumn);
99
+ newIndex = (currentIndex + step) % dataFields.length;
99
100
 
100
101
  while (newIndex < 0) {
101
102
  newIndex += dataFields.length
102
103
  }
103
104
 
104
- me.select(view.getCellId(record, dataFields[newIndex]))
105
+ me.select(view.getCellId(record, dataFields[newIndex]));
106
+
107
+ view.parent.scrollByColumns(currentIndex, step)
105
108
  }
106
109
 
107
110
  /**
@@ -112,10 +115,10 @@ class CellModel extends BaseModel {
112
115
  {view} = me,
113
116
  {store} = view,
114
117
  currentIndex = 0,
115
- dataField, newIndex, record;
118
+ dataField, newIndex;
116
119
 
117
120
  if (me.hasSelection()) {
118
- currentIndex = store.indexOf(view.getRecordByCellId(me.items[0]));
121
+ currentIndex = store.indexOf(view.getRecord(me.items[0]));
119
122
  dataField = view.getDataField(me.items[0])
120
123
  } else {
121
124
  dataField = me.dataFields[0]
@@ -127,10 +130,7 @@ class CellModel extends BaseModel {
127
130
  newIndex += store.getCount()
128
131
  }
129
132
 
130
- record = store.getAt(newIndex);
131
-
132
- me.select(view.getCellId(record, dataField));
133
-
133
+ me.select(view.getCellId(store.getAt(newIndex), dataField));
134
134
  view.scrollByRows(currentIndex, step)
135
135
  }
136
136
 
@@ -1,21 +0,0 @@
1
- .neo-grid-scrollbar {
2
- bottom : 1px;
3
- opacity : 0;
4
- overflow-y : scroll;
5
- position : absolute;
6
- right : 0;
7
- top : 31px; // header-toolbar height
8
- transition : opacity 1s ease-out;
9
- width : 16px;
10
- z-index : 2;
11
-
12
- &:hover {
13
- opacity: 1;
14
- }
15
- }
16
-
17
- .neo-grid-wrapper:has(.neo-grid-view.neo-is-scrolling) {
18
- .neo-grid-scrollbar {
19
- opacity: 1;
20
- }
21
- }