neo.mjs 8.14.2 → 8.15.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.14.2'
23
+ * @member {String} version='8.15.0'
24
24
  */
25
- version: '8.14.2'
25
+ version: '8.15.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-02-04",
19
+ "datePublished": "2025-02-06",
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.14.2'
110
+ html : 'v8.15.0'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -1,4 +1,3 @@
1
- import Component from '../../../src/component/Base.mjs';
2
1
  import ComponentController from '../../../src/controller/Component.mjs';
3
2
  import ComponentManager from '../../../src/manager/Component.mjs';
4
3
  import DemoDialog from './DemoDialog.mjs';
@@ -127,14 +126,15 @@ class MainContainerController extends ComponentController {
127
126
  * @param {Object} proxyRect
128
127
  */
129
128
  dropDialogBetweenWindows(proxyRect) {
130
- let me = this,
131
- dialog = me.dialog,
132
- intersection = Rectangle.getIntersectionDetails(me.dragStartWindowRect, proxyRect),
133
- side = me.dockedWindowSide,
134
- size = proxyRect.height * proxyRect.width,
129
+ let me = this,
130
+ dialog = me.dialog,
131
+ intersection = Rectangle.getIntersection(me.dragStartWindowRect, proxyRect),
132
+ intersectionSize = intersection?.height * intersection?.width,
133
+ side = me.dockedWindowSide,
134
+ size = proxyRect.height * proxyRect.width,
135
135
  wrapperStyle;
136
136
 
137
- if (intersection.area > size / 2) { // drop the dialog fully into the dragStart window
137
+ if (intersectionSize > size / 2) { // drop the dialog fully into the dragStart window
138
138
  me.destroyDockedWindowProxy();
139
139
 
140
140
  wrapperStyle = dialog.wrapperStyle;
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.14.2'
23
+ * @member {String} version='8.15.0'
24
24
  */
25
- version: '8.14.2'
25
+ version: '8.15.0'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "8.14.2",
3
+ "version": "8.15.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -262,12 +262,12 @@ const DefaultConfig = {
262
262
  useVdomWorker: true,
263
263
  /**
264
264
  * buildScripts/injectPackageVersion.mjs will update this value
265
- * @default '8.14.2'
265
+ * @default '8.15.0'
266
266
  * @memberOf! module:Neo
267
267
  * @name config.version
268
268
  * @type String
269
269
  */
270
- version: '8.14.2'
270
+ version: '8.15.0'
271
271
  };
272
272
 
273
273
  Object.assign(DefaultConfig, {
@@ -1,6 +1,7 @@
1
1
  import Base from '../core/Base.mjs';
2
2
  import Filter from './Filter.mjs';
3
3
  import Logger from '../util/Logger.mjs';
4
+ import NeoArray from '../util/Array.mjs';
4
5
  import Observable from '../core/Observable.mjs';
5
6
  import Sorter from './Sorter.mjs';
6
7
 
@@ -1015,6 +1016,25 @@ class Collection extends Base {
1015
1016
  return this._items[this.getCount() -1]
1016
1017
  }
1017
1018
 
1019
+ /**
1020
+ * Moves an item from fromIndex to toIndex
1021
+ * @param {Number} fromIndex
1022
+ * @param {Number} toIndex
1023
+ */
1024
+ move(fromIndex, toIndex) {
1025
+ if (fromIndex === toIndex) {
1026
+ return
1027
+ }
1028
+
1029
+ let {items} = this;
1030
+
1031
+ if (fromIndex >= items.length) {
1032
+ fromIndex = items.length - 1
1033
+ }
1034
+
1035
+ items.splice(toIndex, 0, items.splice(fromIndex, 1)[0])
1036
+ }
1037
+
1018
1038
  /**
1019
1039
  * @param {Object} opts
1020
1040
  * @protected
@@ -272,11 +272,11 @@ class Container extends Component {
272
272
 
273
273
  item.reference = ref;
274
274
  result.push(item);
275
- hasWeight ||= ('weight' in item);
275
+ hasWeight ||= ('weight' in item)
276
276
  }
277
277
 
278
278
  if (hasWeight) {
279
- result.sort(byWeight);
279
+ result.sort(byWeight)
280
280
  }
281
281
 
282
282
  value = result
@@ -413,7 +413,9 @@ class Container extends Component {
413
413
 
414
414
  // We need to add items into the vdom
415
415
  me.updateDepth = -1;
416
- me.update()
416
+ me.update();
417
+
418
+ me.fire('itemsCreated', {id: me.id, items})
417
419
  }
418
420
 
419
421
  /**
@@ -50,9 +50,18 @@ class DragZone extends Base {
50
50
  */
51
51
  bodyCursorStyle: null,
52
52
  /**
53
- * @member {String|null} boundaryContainerId=null
53
+ * Limit the zone in which you can drag an element.
54
+ * You can pass a node id, or an array of 2 node ids, in case you need an intersection.
55
+ * Example for 2 ids: grid.header.Toolbar => boundaryContainerId: [id, me.parent.id]
56
+ * @member {String|String[]|null} boundaryContainerId=null
54
57
  */
55
58
  boundaryContainerId: null,
59
+ /**
60
+ * Stores the DOMRect matching this.boundaryContainerId
61
+ * @member {DOMRect|null} data=null
62
+ * @protected
63
+ */
64
+ boundaryContainerRect: null,
56
65
  /**
57
66
  * Store data which you want to pass to drop related events here
58
67
  * @member {Object|null} data=null
@@ -315,16 +324,18 @@ class DragZone extends Base {
315
324
  {appName, owner, windowId} = me,
316
325
  {cls} = owner,
317
326
  rect = me.getDragElementRect(data),
318
- offsetX, offsetY;
327
+ mainData, offsetX, offsetY;
319
328
 
320
329
  me.setData();
321
330
 
322
- Neo.main.addon.DragDrop.setConfigs({
331
+ mainData = await Neo.main.addon.DragDrop.setConfigs({
323
332
  appName,
324
333
  windowId,
325
334
  ...me.getMainThreadConfigs()
326
335
  });
327
336
 
337
+ me.boundaryContainerRect = mainData.boundaryContainerRect
338
+
328
339
  NeoArray.add(cls, 'neo-is-dragging');
329
340
  owner.cls = cls;
330
341
 
@@ -57,7 +57,7 @@ class SortZone extends BaseSortZone {
57
57
  {view} = grid,
58
58
  gridRows = view.getVdomRoot().cn,
59
59
  columnIndex = me.dragElement['aria-colindex'] - 1,
60
- {dataField} = view.columnPositions[columnIndex],
60
+ {dataField} = view.columnPositions.getAt(columnIndex),
61
61
  cells = view.getColumnCells(dataField),
62
62
  rows = [],
63
63
  config = await super.createDragProxy(data, false),
@@ -95,10 +95,9 @@ class SortZone extends BaseSortZone {
95
95
  config.listeners = {
96
96
  mounted() {
97
97
  Neo.main.DomAccess.scrollTo({
98
- direction: 'top',
99
- id : viewWrapperId,
100
- value : view.scrollPosition.y,
101
- windowId : this.windowId
98
+ id : viewWrapperId,
99
+ value : view.scrollPosition.y,
100
+ windowId: this.windowId
102
101
  })
103
102
  }
104
103
  };
@@ -150,14 +149,17 @@ class SortZone extends BaseSortZone {
150
149
  await super.onDragStart(data);
151
150
 
152
151
  if (this.moveColumnContent) {
153
- let me = this,
154
- {view} = me.owner.parent,
155
- columnIndex = me.dragElement['aria-colindex'] - 1,
156
- {dataField} = view.columnPositions[columnIndex],
157
- cells = view.getColumnCells(dataField);
152
+ let me = this,
153
+ {view} = me.owner.parent,
154
+ columnIndex = me.dragElement['aria-colindex'] - 1,
155
+ columnPosition = view.columnPositions.getAt(columnIndex),
156
+ {dataField} = columnPosition,
157
+ cells = view.getColumnCells(dataField);
158
+
159
+ columnPosition.hidden = true;
158
160
 
159
161
  cells.forEach(cell => {
160
- cell.style.display = 'none'
162
+ cell.style.visibility = 'hidden'
161
163
  });
162
164
 
163
165
  view.update()
@@ -172,33 +174,35 @@ class SortZone extends BaseSortZone {
172
174
  super.switchItems(index1, index2);
173
175
 
174
176
  if (this.moveColumnContent) {
175
- let me = this,
176
- {itemRects} = me,
177
- {view} = me.owner.parent,
178
- columnPositions = view._columnPositions, // no clone
179
- column1Cells = view.getColumnCells(columnPositions[index1].dataField),
180
- column2Cells = view.getColumnCells(columnPositions[index2].dataField);
181
-
182
- Object.assign(columnPositions[index1], {
177
+ let me = this,
178
+ {itemRects} = me,
179
+ {view} = me.owner.parent,
180
+ {columnPositions} = view,
181
+ column1Position = columnPositions.getAt(index1),
182
+ column2Position = columnPositions.getAt(index2),
183
+ column1Cells = view.getColumnCells(column1Position.dataField),
184
+ column2Cells = view.getColumnCells(column2Position.dataField);
185
+
186
+ Object.assign(column1Position, {
183
187
  width: itemRects[index2].width,
184
188
  x : itemRects[index2].x + 1
185
189
  });
186
190
 
187
- Object.assign(columnPositions[index2], {
191
+ Object.assign(column2Position, {
188
192
  width: itemRects[index1].width,
189
193
  x : itemRects[index1].x + 1
190
194
  });
191
195
 
192
- NeoArray.move(columnPositions, index1, index2);
196
+ columnPositions.move(index1, index2);
193
197
 
194
198
  column1Cells.forEach(node => {
195
- node.style.left = columnPositions[index2].x + 'px';
196
- node.style.width = columnPositions[index2].width + 'px'
199
+ node.style.left = column1Position.x + 'px';
200
+ node.style.width = column1Position.width + 'px'
197
201
  });
198
202
 
199
203
  column2Cells.forEach(node => {
200
- node.style.left = columnPositions[index1].x + 'px';
201
- node.style.width = columnPositions[index1].width + 'px'
204
+ node.style.left = column2Position.x + 'px';
205
+ node.style.width = column2Position.width + 'px'
202
206
  });
203
207
 
204
208
  view.update()
@@ -36,8 +36,13 @@ class DragZone extends BaseDragZone {
36
36
  {'drag:start': me.onDragStart, ...opts}
37
37
  ]);
38
38
 
39
- owner.on('insert', me.onItemInsert, me);
39
+ owner.on({
40
+ insert : me.onItemInsert,
41
+ itemsCreated: me.onItemsCreated,
42
+ scope : me
43
+ });
40
44
 
45
+ // The toolbar items can already be created
41
46
  me.adjustToolbarItemCls(true)
42
47
  }
43
48
 
@@ -98,16 +103,25 @@ class DragZone extends BaseDragZone {
98
103
  }
99
104
 
100
105
  /**
101
- * @param {Object} data
102
- * @param {Number} data.index
106
+ * @param {Object} data
107
+ * @param {Number} data.index
103
108
  * @param {Neo.component.Base} data.item
104
109
  */
105
110
  onItemInsert(data) {
106
- let {item} = data,
107
- cls = item.cls || [];
111
+ let {item} = data,
112
+ wrapperCls = item.wrapperCls || [];
113
+
114
+ NeoArray.add(wrapperCls, 'neo-draggable');
115
+ item.wrapperCls = wrapperCls
116
+ }
108
117
 
109
- NeoArray.add(cls, 'neo-draggable');
110
- item.cls = cls
118
+ /**
119
+ * @param {Object} data
120
+ * @param {String} data.id
121
+ * @param {Neo.component.Base[]} data.items
122
+ */
123
+ onItemsCreated(data) {console.log('onItemsCreated');
124
+ this.adjustToolbarItemCls(true)
111
125
  }
112
126
  }
113
127
 
@@ -61,6 +61,14 @@ class SortZone extends DragZone {
61
61
  * @protected
62
62
  */
63
63
  reversedLayoutDirection: false,
64
+ /**
65
+ * @member {Number} scrollLeft=0
66
+ */
67
+ scrollLeft: 0,
68
+ /**
69
+ * @member {Number} scrollTop=0
70
+ */
71
+ scrollTop: 0,
64
72
  /**
65
73
  * Internal flag: onDragStart() will set the value to horizontal or vertical, depending on the current layout.
66
74
  * @member {String} sortDirection='horizontal'
@@ -141,36 +149,53 @@ class SortZone extends DragZone {
141
149
  /**
142
150
  * @param {Object} data
143
151
  */
144
- onDragMove(data) {
145
- if (this.itemRects) { // the method can trigger before we got the client rects from the main thread
146
- let me = this,
147
- moveFactor = 0.55, // we can not use 0.5, since items would jump back & forth
148
- index = me.currentIndex,
149
- {itemRects} = me,
150
- maxItems = itemRects.length - 1,
151
- reversed = me.reversedLayoutDirection,
152
- delta, itemWidth;
153
-
154
- if (me.sortDirection === 'horizontal') {
155
- delta = data.clientX - me.offsetX - itemRects[index].left;
156
- itemWidth = 'width'
157
- } else {
158
- delta = data.clientY - me.offsetY - itemRects[index].top;
159
- itemWidth = 'height'
160
- }
152
+ async onDragMove(data) {
153
+ // the method can trigger before we got the client rects from the main thread
154
+ if (!this.itemRects || this.isScrolling) {
155
+ return
156
+ }
157
+
158
+ let me = this,
159
+ moveFactor = 0.55, // we can not use 0.5, since items would jump back & forth
160
+ index = me.currentIndex,
161
+ {itemRects} = me,
162
+ maxItems = itemRects.length - 1,
163
+ reversed = me.reversedLayoutDirection,
164
+ delta, itemWidth;
165
+
166
+ if (me.sortDirection === 'horizontal') {
167
+ delta = data.clientX + me.scrollLeft - me.offsetX - itemRects[index].left;
168
+ itemWidth = 'width'
169
+ } else {
170
+ delta = data.clientY + me.scrollTop - me.offsetY - itemRects[index].top;
171
+ itemWidth = 'height'
172
+ }
173
+
174
+ if (index > 0 && (!reversed && delta < 0 || reversed && delta > 0)) {
175
+ if (Math.abs(delta) > itemRects[index - 1][itemWidth] * moveFactor) {
176
+ me.currentIndex--;
161
177
 
162
- if (index > 0 && (!reversed && delta < 0 || reversed && delta > 0)) {
163
- if (Math.abs(delta) > itemRects[index - 1][itemWidth] * moveFactor) {
164
- me.currentIndex--;
165
- me.switchItems(index, me.currentIndex)
178
+ if (data.clientX < me.boundaryContainerRect.left) {
179
+ me.isScrolling = true;
180
+ await me.owner.scrollToIndex?.(me.currentIndex, itemRects[me.currentIndex]);
181
+ me.isScrolling = false
166
182
  }
183
+
184
+ me.switchItems(index, me.currentIndex)
167
185
  }
186
+ }
168
187
 
169
- else if (index < maxItems && (!reversed && delta > 0 || reversed && delta < 0)) {
170
- if (Math.abs(delta) > itemRects[index + 1][itemWidth] * moveFactor) {
171
- me.currentIndex++;
172
- me.switchItems(index, me.currentIndex)
188
+ else if (index < maxItems && (!reversed && delta > 0 || reversed && delta < 0)) {
189
+ if (Math.abs(delta) > itemRects[index + 1][itemWidth] * moveFactor) {
190
+ me.currentIndex++;
191
+
192
+ if (data.clientX > me.boundaryContainerRect.right) {
193
+ me.isScrolling = true;
194
+ await me.owner.scrollToIndex?.(me.currentIndex, itemRects[me.currentIndex]);
195
+ me.isScrolling = false
173
196
  }
197
+
198
+ me.switchItems(index, me.currentIndex)
174
199
  }
175
200
  }
176
201
  }
@@ -506,8 +506,11 @@ class GridContainer extends BaseContainer {
506
506
  /**
507
507
  * @param {Object} data
508
508
  */
509
- onScroll(data) {
510
- this.view.scrollPosition = {x: data.scrollLeft, y: this.view.scrollPosition.y}
509
+ onScroll({scrollLeft}) {
510
+ let me = this;
511
+
512
+ me.headerToolbar.scrollLeft = scrollLeft;
513
+ me.view.scrollPosition = {x: scrollLeft, y: me.view.scrollPosition.y}
511
514
  }
512
515
 
513
516
  /**
package/src/grid/View.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import ClassSystemUtil from '../util/ClassSystem.mjs';
2
+ import Collection from '../collection/Base.mjs';
2
3
  import Component from '../component/Base.mjs';
3
4
  import NeoArray from '../util/Array.mjs';
4
5
  import RowModel from '../selection/grid/RowModel.mjs';
@@ -63,10 +64,10 @@ class GridView extends Component {
63
64
  */
64
65
  containerWidth_: 0,
65
66
  /**
66
- * @member {Object[]} columnPositions_=[]
67
+ * @member {Neo.collection.Base|null} columnPositions_=null
67
68
  * @protected
68
69
  */
69
- columnPositions_: [],
70
+ columnPositions_: null,
70
71
  /**
71
72
  * @member {Boolean} highlightModifiedCells_=false
72
73
  */
@@ -238,19 +239,7 @@ class GridView extends Component {
238
239
  * @protected
239
240
  */
240
241
  afterSetContainerWidth(value, oldValue) {
241
- if (value > 0 && this.columnPositions.length > 0) {
242
- this.updateVisibleColumns()
243
- }
244
- }
245
-
246
- /**
247
- * Triggered after the columnPositions config got changed
248
- * @param {Object[]} value
249
- * @param {Object[]} oldValue
250
- * @protected
251
- */
252
- afterSetColumnPositions(value, oldValue) {
253
- if (value.length > 0 && this.containerWidth > 0) {
242
+ if (value > 0) {
254
243
  this.updateVisibleColumns()
255
244
  }
256
245
  }
@@ -299,7 +288,7 @@ class GridView extends Component {
299
288
  {bufferRowRange} = me,
300
289
  newStartIndex;
301
290
 
302
- if (value.x !== oldValue?.x && me.columnPositions.length > 0) {
291
+ if (value.x !== oldValue?.x) {
303
292
  me.updateVisibleColumns()
304
293
  }
305
294
 
@@ -461,6 +450,22 @@ class GridView extends Component {
461
450
  return cellConfig
462
451
  }
463
452
 
453
+ /**
454
+ * Triggered when accessing the columnPositions config
455
+ * @param {Object} value
456
+ * @protected
457
+ */
458
+ beforeGetColumnPositions(value) {
459
+ if (!value) {
460
+ this._columnPositions = value = Neo.create({
461
+ module : Collection,
462
+ keyProperty: 'dataField'
463
+ })
464
+ }
465
+
466
+ return value
467
+ }
468
+
464
469
  /**
465
470
  * Triggered before the selectionModel config gets changed.
466
471
  * @param {Neo.selection.Model} value
@@ -490,7 +495,7 @@ class GridView extends Component {
490
495
  columns = gridContainer.headerToolbar.items,
491
496
  id = me.getRowId(record, rowIndex),
492
497
  trCls = me.getTrClass(record, rowIndex),
493
- config, column, endIndex, gridRow, i, startIndex;
498
+ config, column, columnPosition, endIndex, gridRow, i, startIndex;
494
499
 
495
500
  if (rowIndex % 2 !== 0) {
496
501
  trCls.push('neo-even')
@@ -529,10 +534,17 @@ class GridView extends Component {
529
534
  config.cls = ['neo-locked', ...config.cls || []]
530
535
  }
531
536
 
537
+ columnPosition = me.columnPositions.get(column.dataField);
538
+
532
539
  config.style = {
533
540
  ...config.style,
534
- left : me.columnPositions[i].x + 'px',
535
- width: me.columnPositions[i].width + 'px'
541
+ left : columnPosition.x + 'px',
542
+ width: columnPosition.width + 'px'
543
+ }
544
+
545
+ // Happens during a column header drag OP, when leaving the painted range
546
+ if (columnPosition.hidden) {
547
+ config.style.visibility = 'hidden'
536
548
  }
537
549
 
538
550
  gridRow.cn.push(config)
@@ -552,11 +564,11 @@ class GridView extends Component {
552
564
  endIndex, i;
553
565
 
554
566
  if (
555
- countRecords < 1 ||
556
- me.availableRows < 1 ||
557
- me._containerWidth < 1 || // we are not checking me.containerWidth, since we want to ignore the config symbol
558
- me.columnPositions.length < 1 ||
559
- me.visibleColumns[1] < 1
567
+ countRecords < 1 ||
568
+ me.availableRows < 1 ||
569
+ me._containerWidth < 1 || // we are not checking me.containerWidth, since we want to ignore the config symbol
570
+ me.columnPositions.getCount() < 1 ||
571
+ me.visibleColumns[1] < 1
560
572
  ) {
561
573
  return
562
574
  }
@@ -652,13 +664,14 @@ class GridView extends Component {
652
664
  * @returns {Object[]}
653
665
  */
654
666
  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;
667
+ let me = this,
668
+ cells = [],
669
+ columnIndex = -1,
670
+ vdomRoot = me.getVdomRoot(),
671
+ firstRow = vdomRoot.cn[0],
672
+ i = 0,
673
+ len = firstRow.cn.length,
674
+ cell;
662
675
 
663
676
  // Columns might get moved via drag&drop, so let's check for the current match
664
677
  for (; i < len; i++) {
@@ -668,9 +681,12 @@ class GridView extends Component {
668
681
  }
669
682
  }
670
683
 
671
- vdomRoot.cn.forEach(row => {
672
- cells.push(row.cn[columnIndex])
673
- });
684
+ if (columnIndex > -1) {
685
+ vdomRoot.cn.forEach(row => {
686
+ cell = row.cn[columnIndex];
687
+ cell && cells.push(cell)
688
+ })
689
+ }
674
690
 
675
691
  return cells
676
692
  }
@@ -900,12 +916,16 @@ class GridView extends Component {
900
916
  {columnPositions} = me,
901
917
  {x} = me.scrollPosition,
902
918
  i = 0,
903
- len = columnPositions.length,
919
+ len = columnPositions.getCount(),
904
920
  endIndex = len - 1,
905
921
  column, startIndex;
906
922
 
923
+ if (len < 1) {
924
+ return
925
+ }
926
+
907
927
  for (; i < len; i++) {
908
- column = columnPositions[i];
928
+ column = columnPositions.getAt(i);
909
929
 
910
930
  if (x >= column.x && x <= column.x + column.width) {
911
931
  startIndex = i
@@ -34,6 +34,10 @@ class Toolbar extends BaseToolbar {
34
34
  * @member {String} role='row'
35
35
  */
36
36
  role: 'row',
37
+ /**
38
+ * @member {Number} scrollLeft_=0
39
+ */
40
+ scrollLeft_: 0,
37
41
  /**
38
42
  * @member {Boolean} showHeaderFilters_=false
39
43
  */
@@ -62,13 +66,14 @@ class Toolbar extends BaseToolbar {
62
66
 
63
67
  if (value && !me.sortZone) {
64
68
  import('../../draggable/grid/header/toolbar/SortZone.mjs').then(module => {
65
- let {appName, id, windowId} = me;
69
+ let {appName, id, scrollLeft, windowId} = me;
66
70
 
67
71
  me.sortZone = Neo.create({
68
72
  module : module.default,
69
73
  appName,
70
- boundaryContainerId: id,
74
+ boundaryContainerId: [id, me.parent.id],
71
75
  owner : me,
76
+ scrollLeft,
72
77
  windowId,
73
78
  ...me.sortZoneConfig
74
79
  })
@@ -108,6 +113,18 @@ class Toolbar extends BaseToolbar {
108
113
  }
109
114
  }
110
115
 
116
+ /**
117
+ * Triggered after the scrollLeft config got changed
118
+ * @param {Number} value
119
+ * @param {Number} oldValue
120
+ * @protected
121
+ */
122
+ afterSetScrollLeft(value, oldValue) {
123
+ if (oldValue !== undefined && this.sortZone) {
124
+ this.sortZone.scrollLeft = value
125
+ }
126
+ }
127
+
111
128
  /**
112
129
  * Triggered after the sortable config got changed
113
130
  * @param {Boolean} value
@@ -184,6 +201,7 @@ class Toolbar extends BaseToolbar {
184
201
  async passSizeToView(silent=false) {
185
202
  let me = this,
186
203
  {items} = me,
204
+ {view} = me.parent,
187
205
  rects = await me.getDomRect(items.map(item => item.id)),
188
206
  lastItem = rects[rects.length - 1],
189
207
  columnPositions = rects.map((item, index) => ({dataField: items[index].dataField, width: item.width, x: item.x - rects[0].x})),
@@ -204,12 +222,29 @@ class Toolbar extends BaseToolbar {
204
222
  await me.timeout(100);
205
223
  await me.passSizeToView(silent)
206
224
  } else {
207
- me.parent.view[silent ? 'setSilent' : 'set']({
208
- availableWidth: lastItem.x + lastItem.width - rects[0].x,
209
- columnPositions
210
- })
225
+ view.columnPositions.clear();
226
+ view.columnPositions.add(columnPositions);
227
+
228
+ view[silent ? 'setSilent' : 'set']({
229
+ availableWidth: lastItem.x + lastItem.width - rects[0].x
230
+ });
231
+
232
+ !silent && view.updateVisibleColumns()
211
233
  }
212
234
  }
235
+
236
+ /**
237
+ * @param {Number} index
238
+ * @param {DOMRect} itemRect
239
+ * @returns {Promise<void>}
240
+ */
241
+ async scrollToIndex(index, itemRect) {
242
+ await Neo.main.DomAccess.scrollIntoView({
243
+ delay : 125,
244
+ id : this.items[index].id,
245
+ windowId: this.windowId
246
+ })
247
+ }
213
248
  }
214
249
 
215
250
  export default Neo.setupClass(Toolbar);
@@ -464,7 +464,7 @@ class DomAccess extends Base {
464
464
  returnData;
465
465
 
466
466
  if (Array.isArray(data.id)) {
467
- return data.id.map(id => me.getBoundingClientRect({ id }));
467
+ return data.id.map(id => me.getBoundingClientRect({id}))
468
468
  } else {
469
469
  let node = me.getElementOrBody(data.nodeType ? data : data.id),
470
470
  rect = {},
@@ -897,19 +897,16 @@ class DomAccess extends Base {
897
897
  * @param {String} [data.id]
898
898
  * @param {String} data.behavior='smooth'
899
899
  * @param {String} data.block='start'
900
+ * @param {Number} data.delay=500
900
901
  * @param {String} data.inline='nearest'
901
902
  * @param {String} [data.querySelector]
902
903
  * @returns {Promise<any>}
903
904
  */
904
- scrollIntoView(data) {
905
- let node = data.id ? this.getElement(data.id) : document.querySelector(data.querySelector),
906
- opts = {
907
- behavior: data.behavior || 'smooth',
908
- block : data.block || 'start',
909
- inline : data.inline || 'nearest'
910
- };
905
+ scrollIntoView({id, behavior='smooth', block='start', delay=500, inline='nearest', querySelector}) {
906
+ let node = id ? this.getElement(id) : document.querySelector(querySelector),
907
+ opts = {behavior, block, inline};
911
908
 
912
- if (opts.behavior !== 'smooth') {
909
+ if (behavior !== 'smooth') {
913
910
  node.scrollIntoView(opts)
914
911
  } else {
915
912
  // scrollIntoView() does not provide a callback yet.
@@ -918,11 +915,11 @@ class DomAccess extends Base {
918
915
  if (node) {
919
916
  let hasListener = 'scrollend' in window;
920
917
 
921
- hasListener && document.addEventListener('scrollend', () =>resolve(), {capture : true, once: true});
918
+ hasListener && document.addEventListener('scrollend', () =>resolve(), {capture: true, once: true});
922
919
 
923
920
  node.scrollIntoView(opts);
924
921
 
925
- !hasListener && this.timeout(500).then(() => {resolve()})
922
+ !hasListener && this.timeout(delay).then(() => {resolve()})
926
923
  } else {
927
924
  resolve()
928
925
  }
@@ -932,19 +929,19 @@ class DomAccess extends Base {
932
929
 
933
930
  /**
934
931
  * @param {Object} data
935
- * @param {String} data.direction left, top
932
+ * @param {String} data.direction='top' left, top
936
933
  * @param {String} data.id
937
934
  * @param {Number} data.value
938
935
  * @returns {Object} obj.id => the passed id
939
936
  */
940
- scrollTo(data) {
941
- let node = this.getElement(data.id);
937
+ scrollTo({direction='top', id, value}) {
938
+ let node = this.getElement(id);
942
939
 
943
940
  if (node) {
944
- node[`scroll${Neo.capitalize(data.direction)}`] = data.value
941
+ node[`scroll${Neo.capitalize(direction)}`] = value
945
942
  }
946
943
 
947
- return {id: data.id}
944
+ return {id}
948
945
  }
949
946
 
950
947
  /**
@@ -1,6 +1,7 @@
1
1
  import Base from './Base.mjs';
2
2
  import DomAccess from '../DomAccess.mjs';
3
3
  import DomEvents from '../DomEvents.mjs';
4
+ import Rectangle from '../../util/Rectangle.mjs';
4
5
 
5
6
  /**
6
7
  * @class Neo.main.addon.DragDrop
@@ -438,23 +439,30 @@ class DragDrop extends Base {
438
439
  /**
439
440
  * DragZones will set these configs inside their dragStart() method.
440
441
  * They only persist until the end of a drag OP.
441
- * @param {Object} data
442
- * @param {Boolean} data.alwaysFireDragMove
443
- * @param {String} data.boundaryContainerId
444
- * @param {String} data.scrollContainerId
445
- * @param {Number} data.scrollFactorLeft
446
- * @param {Number} data.scrollFactorTop
442
+ * @param {Object} data
443
+ * @param {Boolean} data.alwaysFireDragMove
444
+ * @param {String|String[]|null} data.boundaryContainerId
445
+ * @param {String|null} data.scrollContainerId
446
+ * @param {Number} data.scrollFactorLeft
447
+ * @param {Number} data.scrollFactorTop
448
+ * @returns {Object} return the boundaryContainerRect
447
449
  */
448
450
  setConfigs(data) {
449
- let me = this,
450
- node;
451
+ let me = this,
452
+ {boundaryContainerId} = data,
453
+ node, rects;
451
454
 
452
455
  delete data.appName;
453
456
  delete data.windowId;
454
457
 
455
- if (data.boundaryContainerId) {
456
- node = DomAccess.getElementOrBody(data.boundaryContainerId);
457
- me.boundaryContainerRect = node.getBoundingClientRect()
458
+ if (boundaryContainerId) {
459
+ rects = DomAccess.getBoundingClientRect({id: boundaryContainerId});
460
+
461
+ if (Array.isArray(boundaryContainerId)) {
462
+ me.boundaryContainerRect = Rectangle.getIntersection(...rects)
463
+ } else {
464
+ me.boundaryContainerRect = rects
465
+ }
458
466
  }
459
467
 
460
468
  delete data.boundaryContainerId;
@@ -489,6 +497,10 @@ class DragDrop extends Base {
489
497
  }
490
498
  })
491
499
  }
500
+
501
+ return {
502
+ boundaryContainerRect: me.boundaryContainerRect || null
503
+ }
492
504
  }
493
505
 
494
506
  /**
@@ -111,30 +111,24 @@ export default class Rectangle extends DOMRect {
111
111
  }
112
112
 
113
113
  /**
114
- * Returns the overlapping area of rect1 & rect2
115
- * @param {Object} rect1
116
- * @param {Object} rect2
117
- * @returns {Number} The area (x * y)
114
+ * Returns the overlapping area of rect1 & rect2 as a new Rectangle
115
+ * @param {DOMRect|Neo.util.Rectangle} rect1
116
+ * @param {DOMRect|Neo.util.Rectangle} rect2
117
+ * @returns {Neo.util.Rectangle|null} The intersecting rect
118
118
  */
119
119
  static getIntersection(rect1, rect2) {
120
- return Rectangle.getIntersectionDetails(rect1, rect2).area;
121
- }
122
-
123
- /**
124
- * Returns the overlapping area of rect1 & rect2
125
- * @param {Object} rect1
126
- * @param {Object} rect2
127
- * @returns {Object} x, y & area
128
- */
129
- static getIntersectionDetails(rect1, rect2) {
130
- let width = Math.max(0, Math.min(rect1.right, rect2.right) - Math.max(rect1.left, rect2.left)),
131
- height = Math.max(0, Math.min(rect1.bottom, rect2.bottom) - Math.max(rect1.top, rect2.top));
120
+ let x = Math.max(rect1.x, rect2.x),
121
+ y = Math.max(rect1.y, rect2.y),
122
+ right = Math.min(rect1.right, rect2.right),
123
+ bottom = Math.min(rect1.bottom, rect2.bottom),
124
+ width = Math.max(0, right - x),
125
+ height = Math.max(0, bottom - y);
126
+
127
+ if (height < 1 || width < 1) {
128
+ return null
129
+ }
132
130
 
133
- return {
134
- area: height * width,
135
- height,
136
- width
137
- };
131
+ return new Rectangle(x, y, width, height)
138
132
  }
139
133
 
140
134
  /**