bm-core-ui 2.11.9-beta.2 → 2.12.0-beta.4

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 (60) hide show
  1. package/README.md +1 -1
  2. package/build/@types/index.d.ts +2194 -94
  3. package/build/BMCollectionView/BMCollectionView.js +977 -196
  4. package/build/BMCollectionView/BMCollectionViewFlowLayout.js +11 -4
  5. package/build/BMCoreUI.css +133 -2
  6. package/build/BMView/BMDragSession.js +1150 -0
  7. package/build/BMView/BMDragSessionActions.js +121 -0
  8. package/build/BMView/BMDragSessionPreview.js +1631 -0
  9. package/build/BMView/BMLayoutConstraint_v2.5.js +14 -59
  10. package/build/BMView/BMView_v2.5.js +34 -3
  11. package/build/BMWindow/BMPopover/BMPopover.js +35 -20
  12. package/build/BMWindow/BMToolWindow.js +29 -1
  13. package/build/BMWindow/BMWindow.js +128 -65
  14. package/build/images/AlignBottom.png +0 -0
  15. package/build/images/AlignCenterX.png +0 -0
  16. package/build/images/AlignCenterY.png +0 -0
  17. package/build/images/AlignLeading.png +0 -0
  18. package/build/images/AlignTop.png +0 -0
  19. package/build/images/AlignTrailing.png +0 -0
  20. package/build/images/AllConstraints.png +0 -0
  21. package/build/images/BottomConstraint.png +0 -0
  22. package/build/images/CenterXConstraint.png +0 -0
  23. package/build/images/CenterYConstraint.png +0 -0
  24. package/build/images/CoreUI2.png +0 -0
  25. package/build/images/CoreUI2@2x.png +0 -0
  26. package/build/images/Desktop.png +0 -0
  27. package/build/images/DesktopMini.png +0 -0
  28. package/build/images/EqualHeight.png +0 -0
  29. package/build/images/EqualHorizontalSpacing.png +0 -0
  30. package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
  31. package/build/images/EqualVerticalSpacing.png +0 -0
  32. package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
  33. package/build/images/EqualWidth.png +0 -0
  34. package/build/images/HeightConstraint.png +0 -0
  35. package/build/images/InactiveConstraints.png +0 -0
  36. package/build/images/Layout.png +0 -0
  37. package/build/images/LayoutVariables.png +0 -0
  38. package/build/images/LeftConstraint.png +0 -0
  39. package/build/images/OwnConstraints.png +0 -0
  40. package/build/images/Phone.png +0 -0
  41. package/build/images/PhoneLandscape.png +0 -0
  42. package/build/images/PhoneLandscapeMini.png +0 -0
  43. package/build/images/PhoneMini.png +0 -0
  44. package/build/images/PhonePortrait.png +0 -0
  45. package/build/images/PhonePortraitMini.png +0 -0
  46. package/build/images/Properties.png +0 -0
  47. package/build/images/RightConstraint.png +0 -0
  48. package/build/images/SubviewConstraints.png +0 -0
  49. package/build/images/Tablet.png +0 -0
  50. package/build/images/TabletLandscape.png +0 -0
  51. package/build/images/TabletLandscapeMini.png +0 -0
  52. package/build/images/TabletMini.png +0 -0
  53. package/build/images/TabletPortrait.png +0 -0
  54. package/build/images/TabletPortraitMini.png +0 -0
  55. package/build/images/TopConstraint.png +0 -0
  56. package/build/images/WidthConstraint.png +0 -0
  57. package/build/index.js +4 -0
  58. package/lib/@types/BMCoreUI.min.d.ts +2194 -94
  59. package/lib/BMCoreUI.min.js +1 -1
  60. package/package.json +2 -2
@@ -5,8 +5,8 @@ import {BMInset} from '../Core/BMInset'
5
5
  import {BMPointMake} from '../Core/BMPoint'
6
6
  import {BMSizeMake} from '../Core/BMSize'
7
7
  import {BMRect, BMRectMake, BMRectMakeWithNodeFrame, BMRectMakeWithOrigin, BMRectByInterpolatingRect} from '../Core/BMRect'
8
- import {BMIndexPathNone} from '../Core/BMIndexPath'
9
- import {BMAnimateWithBlock, BMAnimationContextBeginStatic, BMAnimationContextGetCurrent, BMAnimationContextAddCompletionHandler, BMAnimationApply, __BMVelocityAnimate, BMHook} from '../Core/BMAnimationContext'
8
+ import {BMIndexPathMakeWithRow, BMIndexPathNone} from '../Core/BMIndexPath'
9
+ import {BMAnimateWithBlock, BMAnimationContextBeginStatic, BMAnimationContextGetCurrent, BMAnimationContextAddCompletionHandler, BMAnimationApply, __BMVelocityAnimate, BMHook, BMAnimationBeginWithDuration} from '../Core/BMAnimationContext'
10
10
  import {BMView, BMViewLayoutQueue} from '../BMView/BMView_v2.5'
11
11
  import {BMCollectionViewLayoutAttributesMakeForCellAtIndexPath, BMCollectionViewLayoutAttributesType, BMCollectionViewLayoutAttributesStyleDefaults, _BMCollectionViewTransitionLayoutAttributesMakeWithSourceAttributes} from './BMCollectionViewLayoutAttributes'
12
12
  import {BMJQueryShim, BMCollectionViewCell, BMCollectionViewCellReuseState} from './BMCollectionViewCell'
@@ -16,6 +16,9 @@ import {IScroll} from '../iScroll/iscroll-probe'
16
16
  import { BMLayoutAttribute } from '../BMView/BMLayoutConstraint_v2.5'
17
17
  import { BMMenuSourceNodeCSSClass } from '../BMView/BMMenu'
18
18
  import { BMKeyboardShortcut, BMKeyboardShortcutModifier } from '../BMWindow/BMKeyboardShortcut'
19
+ import { BMDragSession, BMDragSessionAction, BMDragTransferKind, BMDropSessionAction } from '../BMView/BMDragSession'
20
+ import { BMDragPreview } from '../BMView/BMDragSessionPreview'
21
+ import { BMDragSessionActionKind, BMDropSessionActionKind } from '../BMView/BMDragSessionActions'
19
22
 
20
23
  // When set to YES, this will cause collection view to use static animation contexts when setting up animations
21
24
  const BM_COLLECTION_VIEW_USE_STATIC_CONTEXT = YES;
@@ -78,6 +81,45 @@ export var BMCollectionViewAcceptPolicy = Object.freeze({ // <enum>
78
81
 
79
82
  // @endtype
80
83
 
84
+ // @type BMCollectionViewAcceptRegion
85
+
86
+ /**
87
+ * Constants which describe the region by which a drag and drop gesture can be accepted as it
88
+ * enters the collection view's frame.
89
+ */
90
+ export const BMCollectionViewAcceptRegion = Object.freeze({ // <enum>
91
+
92
+ /**
93
+ * Indicates that the drop session will have the same drop action regardless of where
94
+ * in the collection view's frame it is.
95
+ */
96
+ Anywhere: 'Accept', // <enum>
97
+
98
+ /**
99
+ * Indicates that the drop session's drop action changes based on the cell in which the
100
+ * drop occurs. Drops outside of any cell's frame are rejected. The delegate object is
101
+ * expected to implement the {@link BMCollectionViewDelegate.collectionViewDropSessionDidEnterCell} method
102
+ * provide the appropriate drop action for each cell.
103
+ */
104
+ Cell: 'Cell', // <enum>
105
+
106
+ /**
107
+ * Indicates the drop session's drop action depends on the specific position in which the drop occurs.
108
+ * The delegate object is expected to implement the {@link BMCollectionViewDelegate.collectionViewDropSessionDidUpdate}
109
+ * method to provide the appropriate action as the drop session's position updates.
110
+ */
111
+ Position: 'Position', // <enum>
112
+
113
+ /**
114
+ * Indicates that the collection view cannot process the session's items and no further updates will
115
+ * be provided regarding this drop session.
116
+ */
117
+ Nowhere: 'Reject', // <enum>
118
+
119
+ });
120
+
121
+ // @endtype
122
+
81
123
  // @type BMCollectionView Global Symbols
82
124
 
83
125
  // Set to YES during a drag & drop operation
@@ -128,6 +170,24 @@ var _BMCollectionViewSnappingScrollThreshold = 200; // <Number>
128
170
  */
129
171
  var _BMCollectionViewIdentityComparator = function (o1, o2) { return o1 == o2; }; // <Boolean ^ (AnyObject, AnyObject)>
130
172
 
173
+ /**
174
+ * The number of pixels determining how close to an edge the drag session position must be
175
+ * for a collection view to start scrolling.
176
+ */
177
+ const _BMCollectionViewDragScrollDistance = 32;
178
+
179
+ /**
180
+ * How much to multiply the distance to the edge to obtain the final scrolling speed during
181
+ * a drag and drop gesture.
182
+ */
183
+ const _BMCollectionViewDragScrollMultiplier = 20;
184
+
185
+ /**
186
+ * The number of milliseconds to wait for a data source to insert items asynchronously after a drop
187
+ * before playing the generic drop animation for the drop previews.
188
+ */
189
+ const _BMCollectionViewAsynchronousDropDelay = 200;
190
+
131
191
  // @endtype
132
192
 
133
193
  // @type BMCollectionViewScrollingDirection
@@ -239,7 +299,6 @@ export function BMCollectionView() { // <constructor>
239
299
  BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? Object.create(BMView.prototype) : {}, {
240
300
  constructor: BMCollectionView,
241
301
 
242
- // MARK: BMView overrides
243
302
  //#region BMView overrides
244
303
 
245
304
  // @override - BMView
@@ -809,6 +868,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
809
868
  var customScrollRequired = args && args.customScroll;
810
869
 
811
870
  _BMCollectionViews.set(this, true);
871
+ BMDragSession._dropTargets.set(this, this);
812
872
 
813
873
  this._container = BMJQueryShim.shimWithDOMNode(node);
814
874
  this.layout = new BMCollectionViewFlowLayout();
@@ -2071,6 +2131,30 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2071
2131
  * @param indexPaths <[BMIndexPath<T>]> The index paths of the cell whose size should be measured.
2072
2132
  */
2073
2133
  measureSizesOfCellsAtIndexPaths(indexPaths) {
2134
+ // Skip index paths that have already been measured
2135
+ indexPaths = indexPaths.filter(indexPath => {
2136
+ // Return the cached size if it is available.
2137
+ let identifier;
2138
+ if (this._dataSet.identifierForIndexPath) {
2139
+ identifier = this._dataSet.identifierForIndexPath(indexPath);
2140
+
2141
+ if (this._measures[identifier]) {
2142
+ return NO;
2143
+ }
2144
+ }
2145
+ else {
2146
+ let length = this._measuredIndexPaths.length;
2147
+ for (let i = 0; i < length; i++) {
2148
+ let measure = this._measuredIndexPaths[i];
2149
+ if (measure.indexPath.isEqualToIndexPath(indexPath, {usingComparator: this.identityComparator})) {
2150
+ return NO;
2151
+ }
2152
+ }
2153
+ }
2154
+
2155
+ return YES;
2156
+ });
2157
+
2074
2158
  const queue = BMViewLayoutQueue.layoutQueue();
2075
2159
  const iterators = indexPaths.map(p => this._measuredSizeOfCellAtIndexPathGenerator(p, {layoutQueue: queue, dequeue: NO}));
2076
2160
 
@@ -2094,23 +2178,6 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2094
2178
  if (result.done) return result.value;
2095
2179
  }
2096
2180
 
2097
- // Return the cached size if it is available.
2098
- let identifier;
2099
- if (this._dataSet.identifierForIndexPath) {
2100
- identifier = this._dataSet.identifierForIndexPath(indexPath);
2101
-
2102
- if (this._measures[identifier]) return this._measures[identifier];
2103
- }
2104
- else {
2105
- let length = this._measuredIndexPaths.length;
2106
- for (let i = 0; i < length; i++) {
2107
- let measure = this._measuredIndexPaths[i];
2108
- if (measure.indexPath.isEqualToIndexPath(indexPath, {usingComparator: this.identityComparator})) {
2109
- return this._measuredIndexPaths[i].size;
2110
- }
2111
- }
2112
- }
2113
-
2114
2181
  if (BM_COLLECTION_VIEW_DEBUG_MEASURE) {
2115
2182
  if (!this.__debug__measuring) {
2116
2183
  this.__debug__measuring = YES;
@@ -2682,7 +2749,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2682
2749
 
2683
2750
  cellEventCanDoubleClick = NO;
2684
2751
  if (self.delegate && self.delegate.collectionViewCanDoubleClickCell) {
2685
- cellEventCanDoubleClick = self.delegate.collectionViewCanDoubleClickCell(self, cell);
2752
+ cellEventCanDoubleClick = self.delegate.collectionViewCanDoubleClickCell(self, cell, {withEvent: event});
2686
2753
  }
2687
2754
 
2688
2755
  if (cellEventCanDoubleClick) cellEventDoubleClickTimeout = window.setTimeout(function () {
@@ -2909,6 +2976,8 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2909
2976
  return cell;
2910
2977
 
2911
2978
  },
2979
+
2980
+ // #region Drag and drop
2912
2981
 
2913
2982
  isDragging: NO,
2914
2983
 
@@ -2954,6 +3023,8 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2954
3023
  * @param toIndexPath <BMIndexPath<T>> The suggested index path at which to add the items.
2955
3024
  * @param withDropShadows <Map<AnyObject, DOMNode>> A map containing the link between drop shadows and the items.
2956
3025
  * }
3026
+ * @return <Promise<void>> A promise that resolves when the associated data update
3027
+ * has completed.
2957
3028
  */
2958
3029
  _insertItems: async function (items, args) {
2959
3030
  this._droppedShadows = args.withDropShadows;
@@ -2965,6 +3036,605 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2965
3036
  });
2966
3037
  },
2967
3038
 
3039
+ /**
3040
+ * An array containing the cells whose index paths are part of the current drag session, or
3041
+ * `undefined` while there is no drag session in progress for this collection view.
3042
+ */
3043
+ _draggingCells: undefined, // <[BMCollectionViewCell], nullable>
3044
+
3045
+ /**
3046
+ * A map that keeps track of the association between drag items in a drag session and the cells
3047
+ * whose items they represent.
3048
+ */
3049
+ _cellItemMap: undefined, // <Map<BMDragItem, BMCollectionViewCell>>
3050
+
3051
+ /**
3052
+ * Begins a drag gesture from the specified event. The drag event will move the cell from which
3053
+ * the event originates, or all selected cells if that cell is selected.
3054
+ * @param event <Event> The event triggering this action.
3055
+ * {
3056
+ * @param forCell <BMCollectionViewCell> The cell from which this event originates.
3057
+ * @param touchIdentifier <AnyObject> If this event is a `TouchEvent`, this represents the identifier
3058
+ * of the touch point that will control this drag & drop operation.
3059
+ * }
3060
+ */
3061
+ beginDragWithEvent(event, args) {
3062
+ const cell = args.forCell;
3063
+
3064
+ let cells;
3065
+
3066
+ if (this.isCellAtIndexPathSelected(cell.indexPath)) {
3067
+ cells = [cell];
3068
+ // Sort the index paths in ascending order
3069
+ this._selectedIndexPaths.sort((i1, i2) => i2.section == i1.section ? i1.row - i2.row : i1.section - i2.section).forEach(indexPath => {
3070
+ let retainedCell = this.retainCellForIndexPath(indexPath);
3071
+
3072
+ if (retainedCell != cell) {
3073
+ cells.push(retainedCell);
3074
+ }
3075
+ retainedCell.isDragging = YES;
3076
+
3077
+ retainedCell.node.classList.add('BMCollectionViewCellDragging');
3078
+ });
3079
+ }
3080
+ else {
3081
+ cells = [cell];
3082
+ cell.isDragging = YES;
3083
+ cell.retain();
3084
+
3085
+ cell.node.classList.add('BMCollectionViewCellDragging');
3086
+ }
3087
+
3088
+ this._draggingCells = cells;
3089
+ this._cellItemMap = new Map();
3090
+
3091
+ BMDragSession._beginDragWithEvent(event, {dragDelegate: this, view: this, touchIdentifier: args.touchIdentifier});
3092
+ },
3093
+
3094
+ /**
3095
+ * Creates and returns a fallback drag item for the specified index path if the data source
3096
+ * object cannot provide a customized drag item.
3097
+ * @param indexPath <BMIndexPath> The index path for which to return a drag item.
3098
+ * @return <BMDragItem> A drag item;
3099
+ */
3100
+ _defaultDragItemForIndexPath(indexPath) {
3101
+ const object = this.dataSet.copyOfItem?.(indexPath.object) ?? JSON.parse(JSON.stringify(indexPath.object));
3102
+
3103
+ return {
3104
+ canConformToType(type) {
3105
+ return type == 'default' || type == 'object';
3106
+ },
3107
+
3108
+ itemOfType(type) {
3109
+ if (type != 'default' && type != 'object') {
3110
+ throw new Error(`Cannot provide an item representation of type "${type}"`);
3111
+ }
3112
+
3113
+ return object;
3114
+ }
3115
+ }
3116
+ },
3117
+
3118
+ dragSessionInitialItems(session) {
3119
+ this._draggingIndexPaths = [];
3120
+
3121
+ const items = this._draggingCells.map(cell => {
3122
+ const item = this.dataSet.dragItemForIndexPath?.(cell.indexPath, {session}) ?? this._defaultDragItemForIndexPath(cell.indexPath);
3123
+ this._cellItemMap.set(item, cell);
3124
+ this._draggingIndexPaths.push(cell.indexPath);
3125
+ return item;
3126
+ });
3127
+
3128
+ this._draggingIndexPaths.sort((i1, i2) => i1.section == i2.section ? i1.row - i2.row : i1.section - i2.section);
3129
+
3130
+ return items;
3131
+ },
3132
+
3133
+ dragSessionPreviewForItem(session, item) {
3134
+ const cell = this._cellItemMap.get(item);
3135
+
3136
+ cell.node.classList.remove('BMCollectionViewCellDragging');
3137
+ const preview = BMDragPreview.dragPreviewWithCopyOfSourceNode(cell.node, {forItem: item});
3138
+ cell.node.classList.add('BMCollectionViewCellDragging');
3139
+
3140
+ return preview;
3141
+ },
3142
+
3143
+ /**
3144
+ * The current drag action to use based on the current drag position.
3145
+ */
3146
+ _dragAction: undefined, // <BMDragSessionAction, nullable>
3147
+
3148
+ /**
3149
+ * Set to `YES` while a drag session started by this collection view is in its frame.
3150
+ */
3151
+ _dragSessionInFrame: NO, // <Boolean>
3152
+
3153
+ /**
3154
+ * The amount by which to scroll on the Y axis during the current drag session.
3155
+ */
3156
+ _dragScrollDirectionY: 0, // <Number>
3157
+
3158
+ /**
3159
+ * The amount by which to scroll on the X axis during the current drag session.
3160
+ */
3161
+ _dragScrollDirectionX: 0, // <Number>
3162
+
3163
+ /**
3164
+ * Whenever the pointer moves to the edges of this collection view during a drag session,
3165
+ * this method periodically scrolls the collection view's contents appropriately.
3166
+ */
3167
+ _dragScroll() {
3168
+ // This callback runs continuously during the drag & drop operation - it is removed upon the operation finishing
3169
+ this._scrollFrameIdentifier = window.requestAnimationFrame(() => this._dragScroll());
3170
+
3171
+ // Don't perform the scroll during data updates
3172
+ if (this.isUpdatingData) {
3173
+ return;
3174
+ }
3175
+
3176
+ if (this._dragScrollDirectionX || this._dragScrollDirectionY) {
3177
+ const offset = this.scrollOffset;
3178
+ offset.x = offset.x + this._dragScrollDirectionX;
3179
+ offset.y = offset.y + this._dragScrollDirectionY;
3180
+
3181
+ offset.x = Math.max(0, Math.min(offset.x, this.size.width - this.frame.size.width));
3182
+ offset.y = Math.max(0, Math.min(offset.y, this.size.height - this.frame.size.height));
3183
+
3184
+ this.scrollOffset = offset;
3185
+ }
3186
+ },
3187
+
3188
+ /**
3189
+ * The identifier of the animation frame callback used to scroll this collection during a drag session while the
3190
+ * mouse pointer
3191
+ */
3192
+ _scrollFrameIdentifier: undefined, // <Number, nullable>
3193
+
3194
+ dragSessionDidEnter(session) {
3195
+ this._dragSessionInFrame = YES;
3196
+ this._dragAction = BMDragSessionAction.actionWithKind(BMDragSessionActionKind.Ignore);
3197
+ this._scrollFrameIdentifier = window.requestAnimationFrame(() => this._dragScroll());
3198
+ },
3199
+
3200
+ dragSessionDidUpdate(session) {
3201
+ if (this._dragSessionInFrame) {
3202
+ if (this.delegate?.collectionViewCanReorderItemsAtIndexPaths?.(this, this._draggingIndexPaths, {session}) ?? YES) {
3203
+ // If item movement is supported, determine the new index path to which the items should move
3204
+ // Instruct the data set to move the item, if the new index path does not match the index path of any dragging item
3205
+ const localPoint = this.positionOfDragSession(session);
3206
+ const targetIndexPath = this.indexPathAtPoint(localPoint);
3207
+
3208
+ let canMoveItem = YES;
3209
+ if (!targetIndexPath) {
3210
+ return;
3211
+ }
3212
+
3213
+ // If the target index path is an index path of an item that's part of the session, don't perform the reorder
3214
+ this._draggingIndexPaths.forEach(indexPath => {
3215
+ if (targetIndexPath.row == indexPath.row && targetIndexPath.section == indexPath.section) {
3216
+ canMoveItem = NO;
3217
+ }
3218
+ });
3219
+
3220
+ if (canMoveItem) {
3221
+ if (this.dataSet.moveItemsFromIndexPaths && this._draggingIndexPaths.length > 1) {
3222
+ // Instruct the data set to move the items in bulk if possible
3223
+ const newIndexPaths = this.dataSet.moveItemsFromIndexPaths(this._draggingIndexPaths, {toIndexPath: targetIndexPath});
3224
+ this._draggingIndexPaths = newIndexPaths.sort((i1, i2) => i1.section == i2.section ? i1.row - i2.row : i1.section - i2.section);
3225
+ }
3226
+ else {
3227
+ // Otherwise move them one by one
3228
+ this._draggingIndexPaths.forEach((indexPath, index) => {
3229
+ let destinationIndexPath = targetIndexPath.copy();
3230
+ destinationIndexPath.object = indexPath.object;
3231
+ destinationIndexPath.row += index;
3232
+ if (this.dataSet.moveItemFromIndexPath(indexPath, {toIndexPath: destinationIndexPath})) {
3233
+ this._draggingIndexPaths[index] = destinationIndexPath;
3234
+ }
3235
+ });
3236
+ }
3237
+ }
3238
+ }
3239
+
3240
+ // Determine the scrolling direction
3241
+ const viewportFrame = BMRectMakeWithNodeFrame(this.node);
3242
+ const slope = _BMCollectionViewDragScrollDistance;
3243
+ const multi = _BMCollectionViewDragScrollMultiplier;
3244
+ if (session.position.x - viewportFrame.origin.x < slope) {
3245
+ this._dragScrollDirectionX =
3246
+ (session.position.x - viewportFrame.origin.x - slope) / slope * multi;
3247
+ }
3248
+ else if (viewportFrame.right - session.position.x < slope) {
3249
+ this._dragScrollDirectionX = (slope - viewportFrame.right + session.position.x) / slope * multi;
3250
+ }
3251
+ else {
3252
+ this._dragScrollDirectionX = 0;
3253
+ }
3254
+
3255
+ if (session.position.y - viewportFrame.origin.y < slope) {
3256
+ this._dragScrollDirectionY = (session.position.y - viewportFrame.origin.y - slope) / slope * multi;
3257
+ }
3258
+ else if (viewportFrame.bottom - session.position.y < slope) {
3259
+ this._dragScrollDirectionY = (slope - viewportFrame.bottom + session.position.y) / slope * multi;
3260
+ }
3261
+ else {
3262
+ this._dragScrollDirectionY = 0;
3263
+ }
3264
+ }
3265
+
3266
+ return this._dragAction;
3267
+ },
3268
+
3269
+ dragSessionDidExit(session) {
3270
+ this._dragSessionInFrame = NO;
3271
+ if (this.delegate?.collectionViewCanRemoveItemsAtIndexPaths?.(this, this._draggingIndexPaths, {session})) {
3272
+ const message = this.delegate?.collectionDeleteMessageForIndexPaths?.(this, this._draggingIndexPaths, {session}) ?? 'Remove';
3273
+ this._dragAction = BMDragSessionAction.actionWithKind(BMDragSessionActionKind.Delete, {message});
3274
+ }
3275
+
3276
+ if (this._scrollFrameIdentifier) {
3277
+ this._dragScrollDirectionX = 0;
3278
+ this._dragScrollDirectionY = 0;
3279
+ window.cancelAnimationFrame(this._scrollFrameIdentifier);
3280
+ this._scrollFrameIdentifier = undefined;
3281
+ }
3282
+ },
3283
+
3284
+ dragSessionWillFinish(session) {
3285
+ this.delegate?.collectionViewWillFinishInteractiveMovementForCell?.(this, this._draggingCells[0], {atIndexPath: this._draggingCells[0].indexPath, session});
3286
+ },
3287
+
3288
+ dragSessionPerformMoveForItems(session, items) {
3289
+ const indexPaths = items.map(i => this._cellItemMap.get(i).indexPath).sort((i1, i2) => {
3290
+ return i1.section == i2.section ? i1.row - i2.row : i1.section - i2.section;
3291
+ });
3292
+
3293
+ this.dataSet.removeItemsAtIndexPaths(indexPaths, {session});
3294
+ },
3295
+
3296
+ dragSessionPerformDelete(session) {
3297
+ this.dataSet.removeItemsAtIndexPaths(this._draggingIndexPaths, {session});
3298
+ },
3299
+
3300
+ dragSessionTransferKind(session) {
3301
+ const acceptPolicy = this.delegate?.collectionViewTransferPolicyForItemsAtIndexPaths?.(this, this._draggingIndexPaths, {session});
3302
+ switch (acceptPolicy) {
3303
+ case BMCollectionViewTransferPolicy.Move:
3304
+ return BMDragTransferKind.Move;
3305
+ case BMCollectionViewTransferPolicy.Copy:
3306
+ default:
3307
+ return BMDragTransferKind.Copy;
3308
+ }
3309
+ },
3310
+
3311
+ dragSessionDidFinish(session) {
3312
+ // Remove the dragging class from the cell
3313
+ this._draggingCells.forEach(cell => {
3314
+ cell.node.classList.remove('BMCollectionViewCellDragging');
3315
+ cell.isDragging = NO;
3316
+
3317
+ // Release the cell
3318
+ cell.release();
3319
+ });
3320
+
3321
+ this._draggingCells = undefined;
3322
+ this._cellItemMap = undefined;
3323
+ this._draggingIndexPaths = undefined;
3324
+
3325
+ this.delegate?.collectionViewDidFinishInteractiveMovementForCell?.(this, this._draggingCells[0], {atIndexPath: this._draggingCells[0].indexPath, session});
3326
+ },
3327
+
3328
+ dragSessionRequiresCustomDropAnimationForItems(session, items) {
3329
+ // Collection view only needs to customize the drop animation for partial transfers
3330
+ // where the transfer kind is move
3331
+ if (session.transferKind == BMDragTransferKind.Move && session.action._action == BMDropSessionActionKind.AcceptPartially) {
3332
+ return YES;
3333
+ }
3334
+ return NO;
3335
+ },
3336
+
3337
+ dragSessionAnimateDropWithPreviews(session, previews) {
3338
+ // If this collection view was already waiting for the data set to insert some data from a drop,
3339
+ // play the generic drop animation for those previews
3340
+ if (this._dropPreviews) {
3341
+ for (const preview of this._dropPreviews) {
3342
+ preview.performDrop();
3343
+ }
3344
+ }
3345
+
3346
+ this._dropPreviews = previews.slice();
3347
+ const dropPreviews = this._dropPreviews;
3348
+
3349
+ // After this method returns, the drag session will instruct collection view to perform the move for the
3350
+ // affected items, which should trigger a synchronous data update to use and clear the drag previews
3351
+ queueMicrotask(() => {
3352
+ if (this._dropPreviews == dropPreviews) {
3353
+ this._dropPreviews = undefined;
3354
+ }
3355
+ });
3356
+ },
3357
+
3358
+ /**
3359
+ * The kind of region being tracked during a drop session. `undefined` if a drop session is
3360
+ * not in progress.
3361
+ */
3362
+ _dropSessionRegionKind: undefined, // <BMCollectionViewAcceptRegion, nullable>
3363
+
3364
+ /**
3365
+ * The current drop action to use based on the current drop session position.
3366
+ */
3367
+ _dropAction: undefined, // <BMDropSessionAction, nullable>
3368
+
3369
+ /**
3370
+ * The index path associated with the current drop session, if any.
3371
+ */
3372
+ _dropIndexPath: undefined, // <BMIndexPath, nullable>
3373
+
3374
+ dropSessionCanBegin(session) {
3375
+ this._lastDropIndexPath = undefined;
3376
+ this._dropIndexPath = undefined;
3377
+ this._dropAction = undefined;
3378
+
3379
+ const items = session.items.map(i => i.itemOfType('default'));
3380
+ const canBegin = this.delegate?.collectionViewCanAcceptItems?.(this, items, {session}) ?? NO;
3381
+
3382
+ // The drop session can begin if the response is not `NO` or `.Nowhere`.
3383
+ if (canBegin != NO && canBegin != BMCollectionViewAcceptRegion.Nowhere) {
3384
+ if (canBegin == YES || canBegin == BMCollectionViewAcceptRegion.Anywhere) {
3385
+ // If the drop session can be accepted anywhere, request the drop action from the delegate
3386
+ this._dropAction = this.delegate?.collectionViewDropActionForDropSession?.(this, session);
3387
+ this._dropAction ??= BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Accept);
3388
+ this._dropSessionRegionKind = BMCollectionViewAcceptRegion.Anywhere;
3389
+ }
3390
+ else {
3391
+ this._dropSessionRegionKind = canBegin;
3392
+ }
3393
+
3394
+ return YES;
3395
+ }
3396
+
3397
+ return NO;
3398
+ },
3399
+
3400
+ dropSessionDidEnter(session) {
3401
+ this._dragScrollDirectionX = 0;
3402
+ this._dragScrollDirectionY = 0;
3403
+ this._scrollFrameIdentifier = window.requestAnimationFrame(() => this._dragScroll());
3404
+ },
3405
+
3406
+ dropSessionDidExit(session) {
3407
+ if (this._scrollFrameIdentifier) {
3408
+ this._dragScrollDirectionX = 0;
3409
+ this._dragScrollDirectionY = 0;
3410
+ window.cancelAnimationFrame(this._scrollFrameIdentifier);
3411
+ this._scrollFrameIdentifier = undefined;
3412
+ }
3413
+ },
3414
+
3415
+ /**
3416
+ * Returns the position of the specified drag or drop session relative to the collection view's bounds.
3417
+ * @param session <BMDragSession or BMDropSession> The drag or drop session.
3418
+ * @return <BMPoint> The coordinates relative to the bounds.
3419
+ */
3420
+ positionOfDragSession(session) {
3421
+ const point = session.position;
3422
+
3423
+ // Determine the index path over which this session currently is
3424
+ const viewportFrame = BMRectMakeWithNodeFrame(this.node);
3425
+
3426
+ // Discover the coordinates of the point that are relative to the collection view's bounds
3427
+ let localPoint = BMPointMake(
3428
+ point.x - viewportFrame.origin.x,
3429
+ point.y - viewportFrame.origin.y,
3430
+ );
3431
+ localPoint.x += this.scrollOffset.x;
3432
+ localPoint.y += this.scrollOffset.y;
3433
+
3434
+ return localPoint;
3435
+ },
3436
+
3437
+ /**
3438
+ * Determines the index path at the specified point whose coordinates are relative to the bounds.
3439
+ * @param point <BMPoint> The point.
3440
+ * @return <BMIndexPath, nullable> The index path, if any cell's frame intersects the point,
3441
+ * or `undefined` otherwise.
3442
+ */
3443
+ indexPathAtPoint(point) {
3444
+ // Create a rect around this point with a size of 64 by 64 pixels.
3445
+ const rect = BMRectMake(0, 0, 64, 64);
3446
+ rect.center = point;
3447
+
3448
+ // Request the attributes within the given rect
3449
+ const attributes = this.layout.attributesForElementsInRect(rect);
3450
+
3451
+ // Find the index path to which the cell should move
3452
+ let targetIndexPath;
3453
+ attributes.forEach(attribute => {
3454
+ if (attribute.itemType != BMCollectionViewLayoutAttributesType.Cell) {
3455
+ return;
3456
+ }
3457
+
3458
+ if (attribute.frame.containsPoint(point)) {
3459
+ targetIndexPath = attribute.indexPath.copy();
3460
+ }
3461
+ });
3462
+
3463
+ return targetIndexPath;
3464
+ },
3465
+
3466
+ /**
3467
+ * The most recent index path the current drop session has been over.
3468
+ */
3469
+ _lastDropIndexPath: undefined, // <BMIndexPath>
3470
+
3471
+ dropSessionDidUpdate(session) {
3472
+ const localPoint = this.positionOfDragSession(session);
3473
+ const targetIndexPath = this.indexPathAtPoint(localPoint);
3474
+
3475
+ if (targetIndexPath) {
3476
+ this._lastDropIndexPath = targetIndexPath;
3477
+ }
3478
+
3479
+ if (this._dropSessionRegionKind == BMCollectionViewAcceptRegion.Anywhere) {
3480
+ return this._dropAction;
3481
+ }
3482
+
3483
+ // Determine the scrolling direction
3484
+ const viewportFrame = BMRectMakeWithNodeFrame(this.node);
3485
+ const slope = _BMCollectionViewDragScrollDistance;
3486
+ const multi = _BMCollectionViewDragScrollMultiplier;
3487
+ if (session.position.x - viewportFrame.origin.x < slope) {
3488
+ this._dragScrollDirectionX =
3489
+ (session.position.x - viewportFrame.origin.x - slope) / slope * multi;
3490
+ }
3491
+ else if (viewportFrame.right - session.position.x < slope) {
3492
+ this._dragScrollDirectionX = (slope - viewportFrame.right + session.position.x) / slope * multi;
3493
+ }
3494
+ else {
3495
+ this._dragScrollDirectionX = 0;
3496
+ }
3497
+
3498
+ if (session.position.y - viewportFrame.origin.y < slope) {
3499
+ this._dragScrollDirectionY = (session.position.y - viewportFrame.origin.y - slope) / slope * multi;
3500
+ }
3501
+ else if (viewportFrame.bottom - session.position.y < slope) {
3502
+ this._dragScrollDirectionY = (slope - viewportFrame.bottom + session.position.y) / slope * multi;
3503
+ }
3504
+ else {
3505
+ this._dragScrollDirectionY = 0;
3506
+ }
3507
+
3508
+ // If the index path changes, notify the delegate using the appropriate methods
3509
+ if (!targetIndexPath) {
3510
+ if (this._dropIndexPath) {
3511
+ this._dropIndexPath = undefined;
3512
+ this.delegate?.collectionViewDropSessionDidExitIndexPath?.(this, session, {indexPath: this._dropIndexPath});
3513
+
3514
+ if (this._dropSessionRegionKind == BMCollectionViewAcceptRegion.Cell) {
3515
+ return BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
3516
+ }
3517
+ }
3518
+ }
3519
+ else {
3520
+ let isNewIndexPath = NO;
3521
+ if (!this._dropIndexPath) {
3522
+ isNewIndexPath = YES;
3523
+ }
3524
+ else {
3525
+ if (!this._dropIndexPath.isEqualToIndexPath(targetIndexPath, {usingComparator: this.identityComparator})) {
3526
+ isNewIndexPath = YES;
3527
+ this.delegate?.collectionViewDropSessionDidExitIndexPath?.(this, session, {indexPath: this._dropIndexPath});
3528
+ }
3529
+ }
3530
+
3531
+ this._dropIndexPath = targetIndexPath;
3532
+
3533
+ if (isNewIndexPath) {
3534
+ const action = this.delegate?.collectionViewDropSessionDidEnterIndexPath?.(this, session, {indexPath: this._dropIndexPath});
3535
+
3536
+ if (this._dropSessionRegionKind == BMCollectionViewAcceptRegion.Cell) {
3537
+ return action;
3538
+ }
3539
+ }
3540
+ }
3541
+
3542
+ if (this._dropSessionRegionKind == BMCollectionViewAcceptRegion.Position) {
3543
+ const action = this.delegate?.collectionViewDropSessionDidUpdate?.(this, session, {position: localPoint});
3544
+ return action;
3545
+ }
3546
+ },
3547
+
3548
+ dropSessionPreviewForItem(session, item) {
3549
+ const node = this.delegate?.collectionViewPreviewForDropSession?.(this, session, {item});
3550
+ if (node) {
3551
+ return BMDragPreview.dragPreviewWithPreviewNode(node, {forItem: item});
3552
+ }
3553
+ },
3554
+
3555
+ dropSessionWillFinish(session) {
3556
+ this.delegate?.collectionViewDropSessionWillFinish?.(this, session);
3557
+ },
3558
+
3559
+ dropSessionRequiresCustomDropAnimationForItems(session, items) {
3560
+ return YES;
3561
+ },
3562
+
3563
+ dropSessionTransferKind(session) {
3564
+ const acceptPolicy = this.delegate?.collectionViewAcceptPolicyForItems?.(this, session.items.map(i => i.itemOfType('default')), {session});
3565
+ switch (acceptPolicy) {
3566
+ case BMCollectionViewAcceptPolicy.Move:
3567
+ return BMDragTransferKind.Move;
3568
+ case BMCollectionViewAcceptPolicy.Copy:
3569
+ default:
3570
+ return BMDragTransferKind.Copy;
3571
+ }
3572
+ },
3573
+
3574
+ /**
3575
+ * The drop previews that must be animated at the end of a successful drop session. Only set while performing
3576
+ * the data update associated with accepting items via a drop session.
3577
+ */
3578
+ _dropPreviews: undefined, // <[BMDropPreview], nullable>
3579
+
3580
+ dropSessionPerformDrop(session) {
3581
+ // If this collection view was already waiting for the data set to insert some data from a drop,
3582
+ // play the generic drop animation for those previews
3583
+ if (this._dropPreviews) {
3584
+ for (const preview of this._dropPreviews) {
3585
+ preview.performDrop();
3586
+ }
3587
+ }
3588
+
3589
+ // If there are no items to accept, don't perform any other changes
3590
+ if (!session.dropItems.length) {
3591
+ return;
3592
+ }
3593
+
3594
+ let toIndexPath = this._lastDropIndexPath;
3595
+ if (!toIndexPath) {
3596
+ const sectionCount = this.numberOfSections();
3597
+ const row = this.numberOfObjectsInSectionAtIndex(sectionCount - 1) - 1;
3598
+ const section = sectionCount - 1;
3599
+ toIndexPath = this.indexPathForObjectAtRow(row, {inSectionAtIndex: section}) ?? BMIndexPathMakeWithRow(row, {section});
3600
+ }
3601
+
3602
+ // Instruct the data set to insert the items to the appropriate index path
3603
+ this._dropPreviews = session.dropPreviews.slice();
3604
+
3605
+ const items = session.dropItems.map(i => i.itemOfType('default'));
3606
+ const result = this.dataSet.insertItems(items, {toIndexPath, session});
3607
+
3608
+ if (result) {
3609
+ // If the data set performs the change asynchronously, give it 200ms to trigger the data update, otherwise
3610
+ // play the generic drop animation for the previews
3611
+ const dropPreviews = this._dropPreviews;
3612
+ let resolved = NO;
3613
+ result.then(() => {
3614
+ resolved = YES;
3615
+
3616
+ if (this._dropPreviews == dropPreviews) {
3617
+ this._dropPreviews = undefined;
3618
+ }
3619
+ });
3620
+
3621
+ setTimeout(() => {
3622
+ if (!resolved) {
3623
+ if (this._dropPreviews == dropPreviews) {
3624
+ this._dropPreviews = undefined;
3625
+ }
3626
+
3627
+ for (const preview of dropPreviews) {
3628
+ preview.performDrop();
3629
+ }
3630
+ }
3631
+ }, _BMCollectionViewAsynchronousDropDelay);
3632
+ }
3633
+ else {
3634
+ this._dropPreviews = undefined;
3635
+ }
3636
+ },
3637
+
2968
3638
  /**
2969
3639
  * Begins a drag gesture from the given event. The drag event will move the cell from which
2970
3640
  * the event originates.
@@ -2975,7 +3645,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
2975
3645
  * of the touch point that will control this drag & drop operation.
2976
3646
  * }
2977
3647
  */
2978
- beginDragWithEvent(event, args) {
3648
+ _beginDragWithEvent(event, args) {
2979
3649
  if (this.isDragging) return;
2980
3650
  // TODO Consider splitting up this giant method
2981
3651
 
@@ -3018,7 +3688,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3018
3688
  }
3019
3689
  else {
3020
3690
  cells = [cell];
3021
- cell.isDraggging = YES;
3691
+ cell.isDragging = YES;
3022
3692
  cell.retain();
3023
3693
 
3024
3694
  cell.node.classList.add('BMCollectionViewCellDragging');
@@ -3082,18 +3752,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3082
3752
 
3083
3753
  draggingShadows.push(otherDraggingShadow);
3084
3754
  let index = draggingShadows.length;
3085
-
3086
- // __BMVelocityAnimate(otherDraggingShadow, {
3087
- // rotateZ: ((index) * 60 / cells.length) + 'deg',
3088
- // left: [draggingShadowTargetPoint.x + 'px', sourceRect.origin.x + 'px'],
3089
- // top: [draggingShadowTargetPoint.y + 'py', sourceRect.origin.x + 'px']
3090
- // }, {
3091
- // easing: 'easeInOutQuad',
3092
- // duration: 300,
3093
- // complete() {
3094
- // areDraggingShadowsAnimating = NO;
3095
- // }
3096
- // });
3755
+
3097
3756
  (window.Velocity || $.Velocity).animate(otherDraggingShadow, {tween: 1}, {
3098
3757
  easing: 'easeInOutQuad',
3099
3758
  duration: 300,
@@ -3123,15 +3782,6 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3123
3782
  draggingShadowsToAdd.forEach(shadow => document.body.appendChild(shadow));
3124
3783
  document.body.appendChild(draggingIndicator);
3125
3784
 
3126
- // (window.Velocity || $.Velocity).animate(draggingIndicator, {
3127
- // opacity: [1, 0],
3128
- // scaleX: [1, 0],
3129
- // scaleY: [1, 0]
3130
- // }, {
3131
- // duration: 300,
3132
- // easing: 'easeOutQuad'
3133
- // });
3134
-
3135
3785
  __BMVelocityAnimate(draggingIndicator, {
3136
3786
  opacity: [1, 0],
3137
3787
  scaleX: [1, 0],
@@ -3342,10 +3992,14 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3342
3992
  }
3343
3993
 
3344
3994
  // Temporarily suspend these updates while a data update is in progress
3345
- if (this.isUpdatingData) return event.preventDefault(), event.stopPropagation();
3995
+ if (this.isUpdatingData) {
3996
+ event.preventDefault();
3997
+ event.stopPropagation();
3998
+ return;
3999
+ }
3346
4000
 
3347
4001
 
3348
- // 3. Enabling scrolling while close to the collection view's edges.
4002
+ // 3. Enable scrolling while close to the collection view's edges.
3349
4003
  if (clientX - collectionViewViewportFrame.origin.x < 32) {
3350
4004
  scrollDirectionX = (clientX - collectionViewViewportFrame.origin.x - 32) / 32 * 20;
3351
4005
  }
@@ -3773,7 +4427,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3773
4427
  // Remove the dragging class from the cell
3774
4428
  cells.forEach(cell => {
3775
4429
  cell.node.classList.remove('BMCollectionViewCellDragging');
3776
- cell.isDraggging = NO;
4430
+ cell.isDragging = NO;
3777
4431
 
3778
4432
  // Release the cell
3779
4433
  cell.release();
@@ -4381,7 +5035,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
4381
5035
  * Highlights the index path to the specified direction of the currently highlighted index path.
4382
5036
  * @param arrow <String> The key code of the keyboard arrow that was pressed.
4383
5037
  * {
4384
- * @param withEvent <KeyboardEvent> The event that triggerred this action.
5038
+ * @param withEvent <KeyboardEvent> The event that triggered this action.
4385
5039
  * }
4386
5040
  */
4387
5041
  keyboardArrowPressed(arrow, args) {
@@ -5133,6 +5787,49 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5133
5787
  get dataUpdated() { // <Promise<void>, nullable>
5134
5788
  return this._dataUpdatePromise;
5135
5789
  },
5790
+
5791
+ /**
5792
+ * Finds and returns the drop preview associated with the specified layout attributes, if any exists.
5793
+ * @param attributes <BMCollectionViewLayoutAttributes> Tha attributes for which to find the drop preview.
5794
+ * {
5795
+ * @param previewMap <Map<BMIndexPath<T>, BMDropPreview>, nullable>
5796
+ * An optional mapping between index paths and drop items
5797
+ * used to find the drop preview for the item representation
5798
+ * that the data set actually inserted. The default item
5799
+ * representation will be used if this is not provided.
5800
+ * }
5801
+ * @returns <BMDropPreview, nullable> The drop preview if it was found, `undefined` otherwise.
5802
+ */
5803
+ _dropPreviewForLayoutAttributes: function (attributes, {previewMap}) {
5804
+ if (!this._dropPreviews) {
5805
+ return;
5806
+ }
5807
+
5808
+ let dropPreview;
5809
+
5810
+ if (this._cellItemMap) {
5811
+ for (const [item, cell] of this._cellItemMap.entries()) {
5812
+ if (cell.indexPath.isLooselyEqualToIndexPath(attributes.indexPath, {usingComparator: this.identityComparator})) {
5813
+ return this._dropPreviews.find(p => p.item == item);
5814
+ }
5815
+ }
5816
+ }
5817
+ else if (previewMap) {
5818
+ for (const [indexPath, preview] of previewMap.entries()) {
5819
+ if (indexPath.isEqualToIndexPath(attributes.indexPath, {usingComparator: this.identityComparator})) {
5820
+ dropPreview = preview;
5821
+ break;
5822
+ }
5823
+ }
5824
+ }
5825
+ else {
5826
+ // If the data set did not provide the new index paths, assume that the default representation
5827
+ // was used and instead look for the object
5828
+ dropPreview = this._dropPreviews.find(p => p.item.itemOfType('default') == attributes.indexPath.object);
5829
+ }
5830
+
5831
+ return dropPreview;
5832
+ },
5136
5833
 
5137
5834
  /**
5138
5835
  * Should be invoked when the entire data set is updated in bulk.
@@ -5272,8 +5969,14 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5272
5969
  // Disable scrolling and interaction during update
5273
5970
  this._container[0].style.pointerEvents = 'none';
5274
5971
  var containerScrollOffset = this.scrollOffset;
5275
- if (!this.iScroll) this._container[0].scrollTo(containerScrollOffset.x, containerScrollOffset.y);
5276
- if (this.scrollView) this.scrollView.scrollingEnabled = NO;
5972
+
5973
+ if (!this.iScroll) {
5974
+ this._container[0].scrollTo(containerScrollOffset.x, containerScrollOffset.y);
5975
+ }
5976
+
5977
+ if (this.scrollView) {
5978
+ this.scrollView.scrollingEnabled = NO;
5979
+ }
5277
5980
 
5278
5981
  // Get the new content
5279
5982
  var attributes = this.layout.attributesForElementsInRect(this._bounds);
@@ -5284,6 +5987,12 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5284
5987
  var deletedCells = [];
5285
5988
  var insertedCells = [];
5286
5989
  var movingCells = [];
5990
+
5991
+ // Contains cells that are temporarily hidden due to a drop animation
5992
+ const sessionDropCells = [];
5993
+
5994
+ // Contains moving cells that have associated drop previews
5995
+ const dropMovingCells = [];
5287
5996
 
5288
5997
  var supplementaryViewsToInsert = this.layout.supplementaryViewsToInsert();
5289
5998
  var supplementaryViewsToDelete = this.layout.supplementaryViewsToDelete();
@@ -5297,6 +6006,20 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5297
6006
  var visibleRect = new BMRect(BMPointMake(this._bounds.origin.x + this._offscreenBufferSize, this._bounds.origin.y + this._offscreenBufferSize), this._frame.size.copy());
5298
6007
  var currentVisibleRect = new BMRect(BMPointMake(oldBounds.origin.x + this._offscreenBufferSize, oldBounds.origin.y + this._offscreenBufferSize), this._frame.size.copy());
5299
6008
 
6009
+ // If this update occurs because of a drag & drop operation, try to obtain the associated index paths from the data set
6010
+ let dropPreviewMap;
6011
+ if (this._dropPreviews && this.dataSet.indexPathForDragItem) {
6012
+ dropPreviewMap = new Map();
6013
+
6014
+ for (const preview of this._dropPreviews) {
6015
+ const indexPath = this.dataSet.indexPathForDragItem(preview.item);
6016
+
6017
+ if (indexPath) {
6018
+ dropPreviewMap.set(indexPath, preview);
6019
+ }
6020
+ }
6021
+ }
6022
+
5300
6023
  // Compare the current content to the new content and data set
5301
6024
  for (var i = 0; i < oldCells.length; i++) {
5302
6025
  var cell = oldCells[i];
@@ -5314,13 +6037,17 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5314
6037
  var attribute = attributes[j];
5315
6038
 
5316
6039
  // Skip cells with different item types
5317
- if (cell.itemType !== attribute.itemType) continue;
6040
+ if (cell.itemType !== attribute.itemType) {
6041
+ continue;
6042
+ }
5318
6043
 
5319
6044
  // Two cells refer to the same object if they are the same type and their index paths are loosely equal
5320
6045
  if (cell.indexPath.isLooselyEqualToIndexPath(attribute.indexPath, {usingComparator: this.identityComparator})) {
5321
6046
 
5322
6047
  // For supplementary views, it is also required that they have the same type identifier
5323
- if (cell.itemType === BMCollectionViewLayoutAttributesType.SupplementaryView && cell.reuseIdentifier !== attribute.identifier) continue;
6048
+ if (cell.itemType === BMCollectionViewLayoutAttributesType.SupplementaryView && cell.reuseIdentifier !== attribute.identifier) {
6049
+ continue;
6050
+ }
5324
6051
 
5325
6052
  // If they are equal, mark the cell as moving and continue
5326
6053
  cell.targetAttributes = attribute;
@@ -5330,17 +6057,35 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5330
6057
 
5331
6058
  // Instruct the data set to update this cell's content
5332
6059
  if (cell.itemType === BMCollectionViewLayoutAttributesType.Cell) {
5333
- if (this.dataSet.updateCell) this.dataSet.updateCell(cell, {atIndexPath: attribute.indexPath});
6060
+
6061
+ // If a drop preview exists for this cell, store it so that the drop is performed
6062
+ // at the appropriate position
6063
+ if (this._dropPreviews) {
6064
+ const dropPreview = this._dropPreviewForLayoutAttributes(cell.targetAttributes, {previewMap: dropPreviewMap});
6065
+
6066
+ if (dropPreview) {
6067
+ cell._dropPreview = dropPreview;
6068
+ dropMovingCells.push(cell);
6069
+ }
6070
+ }
6071
+
6072
+ if (this.dataSet.updateCell) {
6073
+ this.dataSet.updateCell(cell, {atIndexPath: attribute.indexPath});
6074
+ }
5334
6075
  }
5335
6076
  else if (cell.itemType === BMCollectionViewLayoutAttributesType.SupplementaryView) {
5336
- if (this.dataSet.updateSupplementaryView) this.dataSet.updateSupplementaryView(cell, {withIdentifier: cell.reuseIdentifier, atIndexPath: attribute.indexPath});
6077
+ if (this.dataSet.updateSupplementaryView) {
6078
+ this.dataSet.updateSupplementaryView(cell, {withIdentifier: cell.reuseIdentifier, atIndexPath: attribute.indexPath});
6079
+ }
5337
6080
  }
5338
6081
 
5339
6082
  break;
5340
6083
  }
5341
6084
  }
5342
6085
 
5343
- if (foundCell) continue;
6086
+ if (foundCell) {
6087
+ continue;
6088
+ }
5344
6089
 
5345
6090
  // If the cell wasn't found in the new layout, check to see if its object still exists in the data set
5346
6091
  if (cell.itemType === BMCollectionViewLayoutAttributesType.Cell) {
@@ -5351,9 +6096,22 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5351
6096
  var attribute = this.layout.attributesForCellAtIndexPath(indexPath);
5352
6097
  cell.targetAttributes = attribute;
5353
6098
  movingCells.push(cell);
6099
+
6100
+ // If a drop preview exists for this cell, store it so that the drop is performed
6101
+ // at the appropriate position
6102
+ if (this._dropPreviews) {
6103
+ const dropPreview = this._dropPreviewForLayoutAttributes(cell.targetAttributes, {previewMap: dropPreviewMap});
6104
+
6105
+ if (dropPreview) {
6106
+ cell._dropPreview = dropPreview;
6107
+ dropMovingCells.push(cell);
6108
+ }
6109
+ }
5354
6110
 
5355
6111
  // Instruct the data set to update this cell's content
5356
- if (this.dataSet.updateCell) this.dataSet.updateCell(cell, {atIndexPath: attribute.indexPath});
6112
+ if (this.dataSet.updateCell) {
6113
+ this.dataSet.updateCell(cell, {atIndexPath: attribute.indexPath});
6114
+ }
5357
6115
  }
5358
6116
  else {
5359
6117
  // Otherwise the cell was removed from the data set
@@ -5381,7 +6139,9 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5381
6139
  movingCells.push(cell);
5382
6140
 
5383
6141
  // Instruct the data set to update this supplementary view's content
5384
- if (this.dataSet.updateSupplementaryView) this.dataSet.updateSupplementaryView(cell, {withIdentifier: cell.reuseIdentifier, atIndexPath: attribute.indexPath});
6142
+ if (this.dataSet.updateSupplementaryView) {
6143
+ this.dataSet.updateSupplementaryView(cell, {withIdentifier: cell.reuseIdentifier, atIndexPath: attribute.indexPath});
6144
+ }
5385
6145
  }
5386
6146
  else {
5387
6147
  // Otherwise find its final attributes
@@ -5396,12 +6156,6 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5396
6156
 
5397
6157
  // Construct the cells for the new attributes
5398
6158
  var newCells = [];
5399
-
5400
- // If this update occurs because of a drag & drop operation, the collection view's viewport frame will be needed
5401
- var viewportFrame;
5402
- if (this._droppedShadows) {
5403
- viewportFrame = BMRectMakeWithNodeFrame(this._container[0]);
5404
- }
5405
6159
 
5406
6160
  // Compare the remaining new content to the old data set
5407
6161
  for (var i = 0; i < attributes.length; i++) {
@@ -5424,53 +6178,55 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5424
6178
  var oldAttribute = this.layout.initialAttributesForMovingCellFromIndexPath(oldIndexPath, {toIndexPath: attribute.indexPath, withTargetAttributes: attribute});
5425
6179
  attribute.initialAttributes = oldAttribute;
5426
6180
  //oldAttribute.indexPath = attribute.indexPath;
6181
+
6182
+ // If a drop preview exists for this cell, store it so that the drop is performed
6183
+ // at the appropriate position
6184
+ if (this._dropPreviews) {
6185
+ const dropPreview = this._dropPreviewForLayoutAttributes(cell.targetAttributes, {previewMap: dropPreviewMap});
6186
+
6187
+ if (dropPreview) {
6188
+ cell._dropPreview = dropPreview;
6189
+ dropMovingCells.push(cell);
6190
+ }
6191
+ }
5427
6192
  }
5428
6193
  else {
5429
6194
  // Otherwise this is a new element
5430
6195
 
5431
- // Check if there is a drop shadow associated with this element and transition from that shadow
5432
- let droppedShadow;
5433
- if (this._droppedShadows && (droppedShadow = this._droppedShadows.get(attribute.indexPath.object))) {
5434
- var initialAttributes = attribute.copy();
5435
-
5436
- // Figure out the viewport coordinates of the frame
5437
- let frame = attribute.frame.copy();
5438
- frame.offset(-this.scrollOffset.x, -this.scrollOffset.y);
5439
- frame.offset(viewportFrame.left, viewportFrame.top);
5440
-
5441
- // Figure out the frame of the drop shadow
5442
- // Because BMRectMakeWithNodeFrame takes transforms (including rotation) into account,
5443
- // the rotation is temporarily removed to improve the fidelity of the animation
5444
- BMHook(droppedShadow.node, {rotateZ: '0deg'});
5445
- let dropFrame = BMRectMakeWithNodeFrame(droppedShadow.node);
5446
- BMHook(droppedShadow.node, {rotateZ: droppedShadow.rotation + 'deg'});
5447
-
5448
- // Find the transform to be applied to the cell
5449
- let cellTransform = frame.rectWithTransformToRect(dropFrame);
5450
- // Find the reverse transform to be applied to the drop shadow and save it for the frame
5451
- let shadowTransform = dropFrame.rectWithTransformToRect(frame);
5452
- droppedShadow.transform = shadowTransform;
5453
-
5454
- // Offset the frame back into collection view coordinates
5455
- frame.offset(this.scrollOffset.x, this.scrollOffset.y);
5456
- frame.offset(-viewportFrame.left, -viewportFrame.top);
5457
-
5458
- // Apply the transform to the initial attributes
5459
- frame.origin.x += cellTransform.origin.x;
5460
- frame.origin.y += cellTransform.origin.y;
5461
-
5462
- initialAttributes.frame = frame;
5463
- initialAttributes.style.rotateZ = droppedShadow.rotation + 'deg';
5464
- initialAttributes.style.scaleX = cellTransform.size.width;
5465
- initialAttributes.style.scaleY = cellTransform.size.height;
5466
- initialAttributes.style.opacity = 0;
6196
+ // Check if there is a drop preview associated with this element and transition from that preview
6197
+ if (this._dropPreviews) {
6198
+ // Find if a newly inserted index path matches this cell's index path
6199
+ const dropPreview = this._dropPreviewForLayoutAttributes(attribute, {previewMap: dropPreviewMap});
6200
+
6201
+ let initialAttributes = attribute;
6202
+
6203
+ // If a preferred scroll offset is provided by the layout, displace the cell initially so that it
6204
+ // starts out at the position it will be in at the end of the scroll animation, relative to the viewport
6205
+ const displacement = BMPointMake(
6206
+ preferredScrollOffset.x - scrollOffset.x,
6207
+ preferredScrollOffset.y - scrollOffset.y
6208
+ );
6209
+
6210
+ if (displacement.x || displacement.y) {
6211
+ initialAttributes = attribute.copy();
6212
+ initialAttributes.frame = initialAttributes.frame.copy();
6213
+ initialAttributes.frame.offset(displacement.x, displacement.y);
6214
+ }
6215
+
6216
+ if (dropPreview) {
6217
+ newCell._dropPreview = dropPreview;
6218
+ attribute.initialAttributes = initialAttributes;
6219
+ insertedCells.push(newCell);
6220
+ }
6221
+ else {
6222
+ const initialAttributes = this.layout.initialAttributesForAppearingCellAtIndexPath(attribute.indexPath, {withTargetAttributes: attribute});
6223
+ attribute.initialAttributes = initialAttributes;
6224
+ }
5467
6225
  }
5468
6226
  else {
5469
6227
  var initialAttributes = this.layout.initialAttributesForAppearingCellAtIndexPath(attribute.indexPath, {withTargetAttributes: attribute});
6228
+ attribute.initialAttributes = initialAttributes;
5470
6229
  }
5471
-
5472
- attribute.initialAttributes = initialAttributes;
5473
- //initialAttributes.indexPath = attribute.indexPath;
5474
6230
  }
5475
6231
 
5476
6232
  newCell.attributes = attribute.initialAttributes;
@@ -5596,7 +6352,14 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5596
6352
  animationCells[i].release();
5597
6353
 
5598
6354
  // Additionally, unmanage hidden cells
5599
- if (animationCells[i].attributes.isHidden) animationCells[i]._unmanage();
6355
+ if (animationCells[i].attributes.isHidden) {
6356
+ animationCells[i]._unmanage();
6357
+ }
6358
+ }
6359
+
6360
+ // Remove the hiding class from inserted cells
6361
+ for (const cell of sessionDropCells) {
6362
+ cell.node.classList.remove('BMCollectionViewCellInserting');
5600
6363
  }
5601
6364
 
5602
6365
  // Destroy the deleted cells
@@ -5629,56 +6392,6 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5629
6392
 
5630
6393
  };
5631
6394
 
5632
-
5633
-
5634
- // Apply the new scroll offset, if it changed
5635
- /*if (oldBounds.origin.x != self._bounds.origin.x || oldBounds.origin.y != self._bounds.origin.y) {
5636
-
5637
- if (self.scrollView) {
5638
- var currentOffset = self.scrollView.offset.copy();
5639
- var targetOffset = self._bounds.origin.copy();
5640
-
5641
- targetOffset.x += self._offscreenBufferSize;
5642
- targetOffset.y += self._offscreenBufferSize;
5643
-
5644
- animationOptions.progress = function (elements, fraction) {
5645
- var offsetX = currentOffset.x + fraction * (targetOffset.x - currentOffset.x);
5646
- var offsetY = currentOffset.y + fraction * (targetOffset.y - currentOffset.y);
5647
-
5648
- self.scrollView.offset = BMPointMake(offsetX, offsetY);
5649
- }
5650
- }
5651
- else if (self.iScroll) {
5652
- var currentOffset = scrollOffset;
5653
- var targetOffset = self._bounds.origin.copy();
5654
-
5655
- targetOffset.x += self._offscreenBufferSize;
5656
- targetOffset.y += self._offscreenBufferSize;
5657
-
5658
- animationOptions.progress = function (elements, fraction) {
5659
- var offsetX = currentOffset.x + fraction * (targetOffset.x - currentOffset.x);
5660
- var offsetY = currentOffset.y + fraction * (targetOffset.y - currentOffset.y);
5661
-
5662
- self.iScroll._translate(-offsetX, -offsetY);
5663
- }
5664
- }
5665
- else {
5666
- var currentOffset = scrollOffset;
5667
- var targetOffset = self._bounds.origin.copy();
5668
-
5669
- targetOffset.x += self._offscreenBufferSize;
5670
- targetOffset.y += self._offscreenBufferSize;
5671
-
5672
- animationOptions.progress = function (elements, fraction) {
5673
- var offsetX = currentOffset.x + fraction * (targetOffset.x - currentOffset.x);
5674
- var offsetY = currentOffset.y + fraction * (targetOffset.y - currentOffset.y);
5675
-
5676
- self._container[0].scrollTo(offsetX, offsetY);
5677
- }
5678
- }
5679
-
5680
- }*/
5681
-
5682
6395
  // All cells involved in the animation will be retained for the duration of the animation
5683
6396
  // And then released at the end
5684
6397
  var animationCells = [];
@@ -5696,26 +6409,65 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5696
6409
  }
5697
6410
 
5698
6411
  // Apply the drop shadow animations if needed
5699
- if (self._droppedShadows) {
5700
- self._droppedShadows.forEach(shadow => {
5701
- if (shadow.transform) {
5702
- BMHook(shadow.node, {rotateZ: shadow.rotation + 'deg', translateX: '0px', translateY: '0px'});
5703
- let controller = BMAnimationContextGetCurrent().controllerForObject(shadow, {node: shadow.node});
5704
- controller.registerBuiltInProperty('translateX', {withValue: shadow.transform.origin.x + 'px'});
5705
- controller.registerBuiltInProperty('translateY', {withValue: shadow.transform.origin.y + 'px'});
5706
- controller.registerBuiltInProperty('scaleX', {withValue: shadow.transform.size.width});
5707
- controller.registerBuiltInProperty('scaleY', {withValue: shadow.transform.size.height});
5708
- controller.registerBuiltInProperty('rotateZ', {withValue: '0deg'});
5709
- controller.registerBuiltInProperty('opacity', {withValue: 0});
6412
+ if (self._dropPreviews) {
6413
+ for (const cell of insertedCells) {
6414
+ cell._dropPreview.performDropToNode(cell.node);
6415
+
6416
+ const index = self._dropPreviews.indexOf(cell._dropPreview);
6417
+
6418
+ if (index != -1) {
6419
+ self._dropPreviews.splice(index, 1);
5710
6420
  }
5711
- else {
5712
- BMHook(shadow.node, {rotateZ: shadow.rotation + 'deg'});
5713
- let controller = BMAnimationContextGetCurrent().controllerForObject(shadow, {node: shadow.node});
5714
- controller.registerBuiltInProperty('scaleX', {withValue: .33});
5715
- controller.registerBuiltInProperty('scaleY', {withValue: .33});
5716
- controller.registerBuiltInProperty('opacity', {withValue: 0});
6421
+ }
6422
+
6423
+ if (dropMovingCells.length) {
6424
+ // For the items that move, move them instantly to their final positions start the drop
6425
+ // animations, then move them back for the regular animation to play for them
6426
+ BMAnimationContextBeginStatic();
6427
+ const initialAttributeMap = new Map();
6428
+ for (const cell of dropMovingCells) {
6429
+ // Instantly move each cell to its final attributes
6430
+ initialAttributeMap.set(cell, cell.attributes);
6431
+ cell.attributes = cell.targetAttributes;
5717
6432
  }
5718
- });
6433
+ BMAnimationApply();
6434
+
6435
+ // Play all drop animations
6436
+ for (const cell of dropMovingCells) {
6437
+ cell._dropPreview.performDropToNode(cell.node);
6438
+ const index = self._dropPreviews.indexOf(cell._dropPreview);
6439
+
6440
+ if (index != -1) {
6441
+ self._dropPreviews.splice(index, 1);
6442
+ }
6443
+ }
6444
+
6445
+ // Revert each cell to its staging attributes
6446
+ BMAnimationContextBeginStatic();
6447
+ for (const [cell, attributes] of initialAttributeMap) {
6448
+ cell.attributes = attributes;
6449
+ }
6450
+ BMAnimationApply();
6451
+ }
6452
+
6453
+ // For remaining drop previews, play an animation to the target cell if available
6454
+ if (self._cellItemMap) {
6455
+ for (let i = 0; i < self._dropPreviews.length; i++) {
6456
+ const item = self._dropPreviews[i];
6457
+ const cell = self._cellItemMap.get(item);
6458
+
6459
+ if (cell) {
6460
+ self._dropPreviews.performDropToNode(cell.node);
6461
+ self._dropPreviews.splice(i, 1);
6462
+ i--;
6463
+ }
6464
+ }
6465
+ }
6466
+
6467
+ // Play a generic animation for all remaining drop previews without associated cells
6468
+ for (const preview of self._dropPreviews) {
6469
+ preview.performDrop();
6470
+ }
5719
6471
  }
5720
6472
 
5721
6473
  // Apply the new scroll offset, if it changed
@@ -5766,7 +6518,15 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5766
6518
  var cell = newCells[i];
5767
6519
  if (!cell.animatable) continue;
5768
6520
 
5769
- cell.attributes = cell.targetAttributes;
6521
+ if (cell._dropPreview) {
6522
+ cell._dropPreview = undefined;
6523
+ cell.node.classList.add('BMCollectionViewCellInserting');
6524
+ sessionDropCells.push(cell);
6525
+ }
6526
+ else {
6527
+ cell.attributes = cell.targetAttributes;
6528
+ }
6529
+
5770
6530
  cell.animatable = NO;
5771
6531
 
5772
6532
  animationCells.push(cell.retain());
@@ -5803,6 +6563,11 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5803
6563
  if (BMAnimationContextGetCurrent()) {
5804
6564
  animationBlock();
5805
6565
  BMAnimationContextAddCompletionHandler(animationOptions.complete);
6566
+
6567
+ // Clear out all target attributes on all cell after the animation starts
6568
+ for (const cell of this.allCells) {
6569
+ cell.targetAttributes = undefined;
6570
+ }
5806
6571
  }
5807
6572
  else {
5808
6573
  BMAnimateWithBlock(animationBlock, animationOptions);
@@ -5812,7 +6577,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5812
6577
  this.oldBounds = undefined;
5813
6578
 
5814
6579
  // If no cells should be changed, immediately run the completion handler
5815
- if (newCells.length == 0 && oldCells.length == 0) {
6580
+ if (newCells.length == 0 && oldCells.length == 0 && !this._dropPreviews) {
5816
6581
  animationOptions.complete();
5817
6582
  }
5818
6583
 
@@ -5997,9 +6762,16 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5997
6762
  // Scroll to the offset point
5998
6763
  if (options.animated) {
5999
6764
  var self = this;
6000
- return BMAnimateWithBlock(_ => {
6001
- self.scrollOffset = offset;
6002
- }, {duration: 300});
6765
+ const currentContext = BMAnimationContextGetCurrent();
6766
+ if (!currentContext) {
6767
+ BMAnimationBeginWithDuration(300);
6768
+ }
6769
+
6770
+ self.scrollOffset = offset;
6771
+
6772
+ if (!currentContext) {
6773
+ BMAnimationApply();
6774
+ }
6003
6775
  }
6004
6776
  else {
6005
6777
  // Scrolling is performed differently based on whether iScroll is used or not
@@ -6282,6 +7054,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
6282
7054
  var self = this;
6283
7055
 
6284
7056
  _BMCollectionViews.delete(this);
7057
+ BMDragSession._dropTargets.delete(this);
6285
7058
 
6286
7059
  // Unbind the scroll event listener
6287
7060
  if (this.initialized && !this.iScroll) {
@@ -6419,8 +7192,8 @@ class BMCollectionViewDataSourceAdapter {
6419
7192
  return this._dataSet.moveItemsFromIndexPaths(indexPaths, args);
6420
7193
  }
6421
7194
 
6422
- collectionViewRemoveItemsAtIndexPaths(collectionView, indexPaths) {
6423
- return this._dataSet.removeItemsAtIndexPaths(indexPaths);
7195
+ collectionViewRemoveItemsAtIndexPaths(collectionView, indexPaths, args) {
7196
+ return this._dataSet.removeItemsAtIndexPaths(indexPaths, args);
6424
7197
  }
6425
7198
 
6426
7199
  collectionViewInsertItems(collectionView, items, args) {
@@ -6436,6 +7209,18 @@ class BMCollectionViewDataSourceAdapter {
6436
7209
  };
6437
7210
  }
6438
7211
 
7212
+ if (dataSet.dragItemForIndexPath) {
7213
+ adapter.collectionViewDragItemForIndexPath = function (collectionView, indexPath, args) {
7214
+ return dataSet.dragItemForIndexPath(indexPath, args);
7215
+ };
7216
+ }
7217
+
7218
+ if (dataSet.indexPathForDragItem) {
7219
+ adapter.collectionViewIndexPathForDragItem = function (collectionView, item, args) {
7220
+ return dataSet.indexPathForDragItem(item, args);
7221
+ };
7222
+ }
7223
+
6439
7224
  return adapter;
6440
7225
  }
6441
7226
  }
@@ -6472,30 +7257,14 @@ class BMCollectionViewDataSetAdapter {
6472
7257
  return this._dataSource.collectionViewIndexPathForObject(this._collectionView, object);
6473
7258
  }
6474
7259
 
6475
- contentsForCellWithReuseIdentifier(identifier) {
6476
- throw new Error(`contentsForCellWithReuseIdentifier is unsupported when using data source objects`);
6477
- }
6478
-
6479
7260
  cellForItemAtIndexPath(indexPath) {
6480
7261
  return this._dataSource.collectionViewCellForItemAtIndexPath(this._collectionView, indexPath);
6481
7262
  }
6482
7263
 
6483
- contentsForSupplementaryViewWithIdentifier(identifier) {
6484
- throw new Error(`contentsForSupplementaryViewWithIdentifier is unsupported when using data source objects`);
6485
- }
6486
-
6487
7264
  cellForSupplementaryViewWithIdentifier(identifier, args) {
6488
7265
  return this._dataSource.collectionViewCellForSupplementaryViewWithIdentifier(this._collectionView, identifier, args);
6489
7266
  }
6490
7267
 
6491
- updateCell(cell, {atIndexPath: indexPath}) {
6492
- throw new Error(`updateCell is unsupported when using data source objects`);
6493
- }
6494
-
6495
- updateSupplementaryView(view, {withIdentifier: identifier, atIndexPath: indexPath}) {
6496
- throw new Error(`updateSupplementaryView is unsupported when using data source objects`);
6497
- }
6498
-
6499
7268
  useOldData(use) {
6500
7269
  return this._dataSource.collectionViewUseOldData(this._collectionView, use);
6501
7270
  }
@@ -6512,8 +7281,8 @@ class BMCollectionViewDataSetAdapter {
6512
7281
  return this._dataSource.collectionViewMoveItemsFromIndexPaths(this._collectionView, indexPaths, args);
6513
7282
  }
6514
7283
 
6515
- removeItemsAtIndexPaths(indexPaths) {
6516
- return this._dataSource.collectionViewRemoveItemsAtIndexPaths(this._collectionView, indexPaths);
7284
+ removeItemsAtIndexPaths(indexPaths, args) {
7285
+ return this._dataSource.collectionViewRemoveItemsAtIndexPaths(this._collectionView, indexPaths, args);
6517
7286
  }
6518
7287
 
6519
7288
  insertItems(items, args) {
@@ -6529,6 +7298,18 @@ class BMCollectionViewDataSetAdapter {
6529
7298
  };
6530
7299
  }
6531
7300
 
7301
+ if (dataSource.collectionViewDragItemForIndexPath) {
7302
+ adapter.dragItemForIndexPath = function (indexPath, args) {
7303
+ return dataSource.collectionViewDragItemForIndexPath(adapter._collectionView, indexPath, args);
7304
+ };
7305
+ }
7306
+
7307
+ if (dataSource.collectionViewIndexPathForDragItem) {
7308
+ adapter.indexPathForDragItem = function (item, args) {
7309
+ return dataSource.collectionViewIndexPathForDragItem(adapter._collectionView, item, args);
7310
+ };
7311
+ }
7312
+
6532
7313
  return adapter;
6533
7314
  }
6534
7315
  }