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.
- package/README.md +1 -1
- package/build/@types/index.d.ts +2194 -94
- package/build/BMCollectionView/BMCollectionView.js +977 -196
- package/build/BMCollectionView/BMCollectionViewFlowLayout.js +11 -4
- package/build/BMCoreUI.css +133 -2
- package/build/BMView/BMDragSession.js +1150 -0
- package/build/BMView/BMDragSessionActions.js +121 -0
- package/build/BMView/BMDragSessionPreview.js +1631 -0
- package/build/BMView/BMLayoutConstraint_v2.5.js +14 -59
- package/build/BMView/BMView_v2.5.js +34 -3
- package/build/BMWindow/BMPopover/BMPopover.js +35 -20
- package/build/BMWindow/BMToolWindow.js +29 -1
- package/build/BMWindow/BMWindow.js +128 -65
- package/build/images/AlignBottom.png +0 -0
- package/build/images/AlignCenterX.png +0 -0
- package/build/images/AlignCenterY.png +0 -0
- package/build/images/AlignLeading.png +0 -0
- package/build/images/AlignTop.png +0 -0
- package/build/images/AlignTrailing.png +0 -0
- package/build/images/AllConstraints.png +0 -0
- package/build/images/BottomConstraint.png +0 -0
- package/build/images/CenterXConstraint.png +0 -0
- package/build/images/CenterYConstraint.png +0 -0
- package/build/images/CoreUI2.png +0 -0
- package/build/images/CoreUI2@2x.png +0 -0
- package/build/images/Desktop.png +0 -0
- package/build/images/DesktopMini.png +0 -0
- package/build/images/EqualHeight.png +0 -0
- package/build/images/EqualHorizontalSpacing.png +0 -0
- package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
- package/build/images/EqualVerticalSpacing.png +0 -0
- package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
- package/build/images/EqualWidth.png +0 -0
- package/build/images/HeightConstraint.png +0 -0
- package/build/images/InactiveConstraints.png +0 -0
- package/build/images/Layout.png +0 -0
- package/build/images/LayoutVariables.png +0 -0
- package/build/images/LeftConstraint.png +0 -0
- package/build/images/OwnConstraints.png +0 -0
- package/build/images/Phone.png +0 -0
- package/build/images/PhoneLandscape.png +0 -0
- package/build/images/PhoneLandscapeMini.png +0 -0
- package/build/images/PhoneMini.png +0 -0
- package/build/images/PhonePortrait.png +0 -0
- package/build/images/PhonePortraitMini.png +0 -0
- package/build/images/Properties.png +0 -0
- package/build/images/RightConstraint.png +0 -0
- package/build/images/SubviewConstraints.png +0 -0
- package/build/images/Tablet.png +0 -0
- package/build/images/TabletLandscape.png +0 -0
- package/build/images/TabletLandscapeMini.png +0 -0
- package/build/images/TabletMini.png +0 -0
- package/build/images/TabletPortrait.png +0 -0
- package/build/images/TabletPortraitMini.png +0 -0
- package/build/images/TopConstraint.png +0 -0
- package/build/images/WidthConstraint.png +0 -0
- package/build/index.js +4 -0
- package/lib/@types/BMCoreUI.min.d.ts +2194 -94
- package/lib/BMCoreUI.min.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,1150 @@
|
|
|
1
|
+
import { YES, NO, BMExtend } from '../Core/BMCoreUI';
|
|
2
|
+
import { BMPointMake } from '../Core/BMPoint';
|
|
3
|
+
import { BMRectMakeWithNodeFrame } from '../Core/BMRect';
|
|
4
|
+
import { BMDropSessionActionKind, BMDragSessionActionKind, _BMDragSessionIconMap, _BMDragSessionDropActionMap, _BMDragDropSessionAction } from './BMDragSessionActions';
|
|
5
|
+
import { _BMDragPreviewSet, BMDropPreview } from './BMDragSessionPreview';
|
|
6
|
+
|
|
7
|
+
// @type BMDragTransferKind
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A list of constants describing what happens to source objects when they are transferred
|
|
11
|
+
* as part of drag session a different target than the one they were dragged from.
|
|
12
|
+
*/
|
|
13
|
+
export var BMDragTransferKind = Object.freeze({ // <enum>
|
|
14
|
+
/**
|
|
15
|
+
* Indicates that the items should be moved from the source to the target. This must
|
|
16
|
+
* be specified by both the source and the target for the movement to take place.
|
|
17
|
+
*/
|
|
18
|
+
Move: 'Move', // <enum>
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Indicates that a copy of the items will be transferred to the target. If either the
|
|
22
|
+
* target or the source of the transfer specifies this transfer kind, a copy is performed
|
|
23
|
+
* regardless of the transfer kind specified by the other.
|
|
24
|
+
*/
|
|
25
|
+
Copy: 'Copy', // <enum>
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// @endtype
|
|
29
|
+
|
|
30
|
+
// @type BMDragSessionAction
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* An object that describes the action that should be performed for a drag session when it ends.
|
|
34
|
+
*/
|
|
35
|
+
export function BMDragSessionAction() {} // <constructor>
|
|
36
|
+
|
|
37
|
+
BMDragSessionAction.prototype = BMExtend(Object.create(_BMDragDropSessionAction.prototype), {
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The action to perform at the end of the drag session.
|
|
41
|
+
*/
|
|
42
|
+
get action() { // <BMDragSessionActionKind>
|
|
43
|
+
return this._action;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The message to display on the drag indicator describing the action.
|
|
48
|
+
*/
|
|
49
|
+
get message() {
|
|
50
|
+
return this._message;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The HTML message to display on the drag indicator describing the action.
|
|
55
|
+
*/
|
|
56
|
+
get messageHTML() {
|
|
57
|
+
return this._messageHTML;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initializes this drag action with the specified drop action kind. Optionally,
|
|
62
|
+
* a message and an item count override may be provided.
|
|
63
|
+
* @param action <BMDragSessionActionKind> The action to perform at the end of the drag session.
|
|
64
|
+
* {
|
|
65
|
+
* @param message <String, nullable> If specified, an optional message to display on the
|
|
66
|
+
* drag indicator describing the drop action.
|
|
67
|
+
* @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
|
|
68
|
+
* drag indicator describing the drop action. If `message` is
|
|
69
|
+
* also specified, this parameter is not used.
|
|
70
|
+
* }
|
|
71
|
+
* @return <BMDragAction> This drag action.
|
|
72
|
+
*/
|
|
73
|
+
initWithAction(action, args) {
|
|
74
|
+
this._action = action;
|
|
75
|
+
this._message = args?.message;
|
|
76
|
+
if (!args?.message) {
|
|
77
|
+
this._messageHTML = args?.messageHTML;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return this;
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates and initializes a drag action with the specified drag action kind. Optionally,
|
|
88
|
+
* a message and an item override may be provided.
|
|
89
|
+
* @param action <BMDragSessionDropActionKind> The action to perform at the end of the drag session.
|
|
90
|
+
* {
|
|
91
|
+
* @param message <String, nullable> If specified, an optional message to display on the
|
|
92
|
+
* drag indicator describing the drop action.
|
|
93
|
+
* @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
|
|
94
|
+
* drag indicator describing the drop action. If `message` is
|
|
95
|
+
* also specified, this parameter is not used.
|
|
96
|
+
* }
|
|
97
|
+
* @return <BMDragSessionAction> A drag action.
|
|
98
|
+
*/
|
|
99
|
+
BMDragSessionAction.actionWithKind = function (action, args) {
|
|
100
|
+
return new this().initWithAction(action, args);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// @endtype
|
|
104
|
+
|
|
105
|
+
// @type BMDropSessionAction
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* An object that describes the action that should be performed for a drop session when it ends.
|
|
109
|
+
*/
|
|
110
|
+
export function BMDropSessionAction() {} // <constructor>
|
|
111
|
+
|
|
112
|
+
BMDropSessionAction.prototype = BMExtend(Object.create(_BMDragDropSessionAction.prototype), {
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The action to perform at the end of the drag session.
|
|
116
|
+
*/
|
|
117
|
+
get action() { // <BMDragSessionActionKind>
|
|
118
|
+
return this._action;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The message to display on the drag indicator describing the action.
|
|
123
|
+
*/
|
|
124
|
+
get message() {
|
|
125
|
+
return this._message;
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The HTML message to display on the drag indicator describing the action.
|
|
130
|
+
*/
|
|
131
|
+
get messageHTML() {
|
|
132
|
+
return this._messageHTML;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* If specified when the action is `.AcceptPartially`, the drag items that are actually acceptable.
|
|
137
|
+
*/
|
|
138
|
+
get acceptableItems() {
|
|
139
|
+
return this._acceptableItems;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Initializes this drop action with the specified drop action kind. Optionally,
|
|
144
|
+
* a message and an item count override may be provided.
|
|
145
|
+
* @param action <BMDropSessionActionKind> The action to perform at the end of the drop session.
|
|
146
|
+
* {
|
|
147
|
+
* @param message <String, nullable> If specified, an optional message to display on the
|
|
148
|
+
* drag indicator describing the drop action.
|
|
149
|
+
* @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
|
|
150
|
+
* drag indicator describing the drop action. If `message` is
|
|
151
|
+
* also specified, this parameter is not used.
|
|
152
|
+
* @param items <[BMDragItem], nullable> If specified, the items that are acceptable for the current drop target.
|
|
153
|
+
* Requires the `action` to be set to `.AcceptPartially` to take effect.
|
|
154
|
+
* These items must be part of the drag or drop session.
|
|
155
|
+
* }
|
|
156
|
+
* @return <BMDragAction> This drop action.
|
|
157
|
+
*/
|
|
158
|
+
initWithAction(action, args) {
|
|
159
|
+
this._action = action;
|
|
160
|
+
this._message = args?.message;
|
|
161
|
+
if (!args?.message) {
|
|
162
|
+
this._messageHTML = args?.messageHTML;
|
|
163
|
+
}
|
|
164
|
+
if (action == BMDropSessionActionKind.AcceptPartially) {
|
|
165
|
+
this._acceptableItems = args?.items;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return this;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Creates and initializes a drop action with the specified drop action kind. Optionally,
|
|
176
|
+
* a message and an item override may be provided.
|
|
177
|
+
* @param action <BMDragSessionDropActionKind> The action to perform at the end of the drop session.
|
|
178
|
+
* {
|
|
179
|
+
* @param message <String, nullable> If specified, an optional message to display on the
|
|
180
|
+
* drag indicator describing the drop action.
|
|
181
|
+
* @param messageHTML <String, nullable> If specified, an optional HTML message to display on the
|
|
182
|
+
* drag indicator describing the drop action. If `message` is
|
|
183
|
+
* also specified, this parameter is not used.
|
|
184
|
+
* @param items <[BMDragItem], nullable> If specified, the items that are acceptable for the current drop target.
|
|
185
|
+
* Requires the `action` to be set to `.AcceptPartially` to take effect.
|
|
186
|
+
* These items must be part of the drop session.
|
|
187
|
+
* }
|
|
188
|
+
* @return <BMDropSessionAction> A drop action.
|
|
189
|
+
*/
|
|
190
|
+
BMDropSessionAction.actionWithKind = function (action, args) {
|
|
191
|
+
return new this().initWithAction(action, args);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// @endtype
|
|
195
|
+
|
|
196
|
+
// @type BMDragSession implements EventHandlerObject
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* An object that describes an in-progress drag session. Drag sessions are automatically created by
|
|
200
|
+
* views that begin drag operations and supplied to potential drop targets when the drag gesture
|
|
201
|
+
* intersects their frame.
|
|
202
|
+
*
|
|
203
|
+
* To start a drag session, use the static `beginDragWithEvent` method, passing in the starting
|
|
204
|
+
* event and a delegate object providing the contents of the drag session.
|
|
205
|
+
*
|
|
206
|
+
* The session can be used to obtain the items that participate in the drag gesture and to update
|
|
207
|
+
* the state and messaging presented to the user.
|
|
208
|
+
*/
|
|
209
|
+
export function BMDragSession() {} // <constructor>
|
|
210
|
+
|
|
211
|
+
BMDragSession.prototype = {
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The drag delegate of hte object that initiated this drag session.
|
|
215
|
+
*/
|
|
216
|
+
_dragDelegate: undefined, // <BMDragDelegate>
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The view that initiated this drag session.
|
|
220
|
+
*/
|
|
221
|
+
_sourceView: undefined, // <BMView>
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The items participating in this drag session.
|
|
225
|
+
*/
|
|
226
|
+
_items: undefined, // [BMDragItem]
|
|
227
|
+
|
|
228
|
+
get items() {
|
|
229
|
+
return this._items;
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* An array of nodes corresponding to the previews of the items being dragged.
|
|
234
|
+
* There may be fewer previews than items, and the preview positions in this array
|
|
235
|
+
* correspond
|
|
236
|
+
*/
|
|
237
|
+
_itemPreviews: undefined, // [DOMNode]
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The node displaying the items in this drag session.
|
|
241
|
+
*/
|
|
242
|
+
_itemCountNode: undefined, // <DOMNode>
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The current drop action as specified by the source view.
|
|
246
|
+
*/
|
|
247
|
+
_sourceDropAction: undefined, // <BMDragSessionAction>
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The current drop action as specified by the target view.
|
|
251
|
+
*/
|
|
252
|
+
_targetDropAction: undefined, // <BMDropSessionAction, nullable>
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The outcome of ending the drag session at the current position.
|
|
256
|
+
*/
|
|
257
|
+
_dropAction: BMDragSessionAction.actionWithKind(BMDragSessionActionKind.Reject), // <_BMDragDropSessionAction>
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The final drop action at the end of the session. This property is only accessible since the
|
|
261
|
+
* `dragSessionWillFinish` delegate method is invoked.
|
|
262
|
+
*/
|
|
263
|
+
_action: undefined, // <BMDragSessionAction | BMDropSessionAction | undefined>
|
|
264
|
+
|
|
265
|
+
get action() {
|
|
266
|
+
return this._action;
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* The event that started this drag session.
|
|
271
|
+
*/
|
|
272
|
+
_startEvent: undefined, // <MouseEvent or TouchEvent>
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The touch identifier tracked for this drag session if the session
|
|
276
|
+
* was started via a touch event.
|
|
277
|
+
*/
|
|
278
|
+
_touchIdentifier: undefined, // <Number, nullable>
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* The drag delegate object.
|
|
282
|
+
*/
|
|
283
|
+
_delegate: undefined, // <BMDragDelegate>
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Initializes this drag session with the specified initial mouse or touch event
|
|
287
|
+
* and drag delegate object.
|
|
288
|
+
* @param event <MouseEvent or TouchEvent> The initial event that starts this drag session.
|
|
289
|
+
* {
|
|
290
|
+
* @param dragDelegate <BMDragDelegate> The drag delegate object providing information about
|
|
291
|
+
* the items in this drag session.
|
|
292
|
+
* @param view <BMView> The view initiating this drag session.
|
|
293
|
+
* @param touchIdentifier <Number, nullable> If the event is a touch event, the identifier of the
|
|
294
|
+
* touch that should be tracked for the drag session.
|
|
295
|
+
* If not specified, the first touch will be used instead.
|
|
296
|
+
* }
|
|
297
|
+
* @return <BMDragSession> This drag session.
|
|
298
|
+
*/
|
|
299
|
+
_initWithEvent(event, {dragDelegate, view, touchIdentifier}) {
|
|
300
|
+
this._dropSessions = new Map();
|
|
301
|
+
|
|
302
|
+
this._startEvent = event;
|
|
303
|
+
this._delegate = dragDelegate;
|
|
304
|
+
this._sourceView = view;
|
|
305
|
+
|
|
306
|
+
if ('TouchEvent' in window && event instanceof TouchEvent) {
|
|
307
|
+
this._touchIdentifier = touchIdentifier ?? event.changedTouches[0].identifier;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return this;
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* The current position of the drag session, relative to the viewport.
|
|
315
|
+
*/
|
|
316
|
+
_position: BMPointMake(), // <BMPoint>
|
|
317
|
+
|
|
318
|
+
get position() {
|
|
319
|
+
return this._position;
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The drag indicator displaying information about the drag and drop session.
|
|
324
|
+
*/
|
|
325
|
+
_dragIndicator: undefined, // <_BMDragIndicator, nullable>
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* The current drop target view, if any.
|
|
329
|
+
*/
|
|
330
|
+
_dropTarget: undefined, // <BMView, nullable>
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* A map containing the views that represent valid drop targets for this session as keys
|
|
334
|
+
* and the drop sessions that were created for each as the associated values.
|
|
335
|
+
*/
|
|
336
|
+
_dropSessions: undefined, // <Map<BMView, BMDropSession | BMDragSession>>
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* For touch drag sessions, an event handler used to determine when a new drop target
|
|
340
|
+
* has been reached as the touch pointer moves over a new drop target.
|
|
341
|
+
*/
|
|
342
|
+
_touchDropTargetHandler: undefined, // <void ^(TouchEvent), nullable>
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* A dictionary of event handlers that have been attached to the source view for which this
|
|
346
|
+
* drag session was created.
|
|
347
|
+
*/
|
|
348
|
+
_eventHandlers: undefined, // <Dictionary<unknown>>
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The preview set managing the drag previews.
|
|
352
|
+
*/
|
|
353
|
+
_previewSet: undefined, // <_BMDragPreviewSet>
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* An array of drop previews that can be used to customize the drop animation. This property is only
|
|
357
|
+
* initialized at the end of the drag session if the delegate object implemented the
|
|
358
|
+
* `dragSessionRequiresCustomDropAnimationForItems` method and returned `YES` from it when it was
|
|
359
|
+
* invoked.
|
|
360
|
+
*/
|
|
361
|
+
_dropPreviews: undefined, // <[BMDropPreview], nullable>
|
|
362
|
+
|
|
363
|
+
get dropPreviews() {
|
|
364
|
+
return this._dropPreviews;
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The kind of transfer that will be performed. Only set after the `dragSessionWillFinish` delegate
|
|
369
|
+
* method returns if the outcome of the drag and drop gesture is transfer to a different view.
|
|
370
|
+
* `undefined` in all other cases.
|
|
371
|
+
*/
|
|
372
|
+
_transferKind: undefined, // <BMDragTransferKind, nullable>
|
|
373
|
+
|
|
374
|
+
get transferKind() {
|
|
375
|
+
return this._transferKind;
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Sets up the drag previews and indicator and appropriate event handlers and starts tracking
|
|
380
|
+
* a drag originating from the event with which this drag session was initialized.
|
|
381
|
+
*/
|
|
382
|
+
_beginDrag() {
|
|
383
|
+
// Notify the delegate that this drag session started
|
|
384
|
+
this._delegate.dragSessionWillBegin?.(this);
|
|
385
|
+
|
|
386
|
+
const touchIdentifier = this._touchIdentifier;
|
|
387
|
+
|
|
388
|
+
// Create an indicator that shows how many items are being dragged
|
|
389
|
+
|
|
390
|
+
// Determine where the drag started from
|
|
391
|
+
const event = this._startEvent;
|
|
392
|
+
if (touchIdentifier !== undefined) {
|
|
393
|
+
for (var i = 0; i < event.changedTouches.length; i++) {
|
|
394
|
+
if (event.changedTouches[i].identifier == touchIdentifier) {
|
|
395
|
+
this._position = BMPointMake(
|
|
396
|
+
event.changedTouches[i].clientX,
|
|
397
|
+
event.changedTouches[i].clientY,
|
|
398
|
+
);
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
this._position = BMPointMake(event.clientX, event.clientY);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Request the items that participate in this drag session
|
|
408
|
+
this._items = this._delegate.dragSessionInitialItems(this);
|
|
409
|
+
|
|
410
|
+
this._dropSessions.set(this._sourceView, this);
|
|
411
|
+
|
|
412
|
+
// Determine the valid drop targets for these items and create the associated
|
|
413
|
+
// drop session for each view that can accept them
|
|
414
|
+
if (this._delegate.dragSessionCanTransferItems?.(this) ?? YES) {
|
|
415
|
+
for (const [view, dropDelegate] of BMDragSession._dropTargets.entries()) {
|
|
416
|
+
// Skip over the source view if it is also a drop target
|
|
417
|
+
if (view == this._sourceView) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const dropSession = BMDropSession._sessionForDragSession(this, {delegate: dropDelegate});
|
|
422
|
+
if (dropDelegate.dropSessionCanBegin(dropSession)) {
|
|
423
|
+
this._dropSessions.set(view, dropSession);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
this._initDropTargetHandlers();
|
|
430
|
+
|
|
431
|
+
// Determine the offset from the center, if the initial item provides a preview source,
|
|
432
|
+
// otherwise default to centering the drag preview on the pointer
|
|
433
|
+
const firstItemPreview = this._delegate.dragSessionPreviewForItem(this, this._items[0]);
|
|
434
|
+
let pointerOffset = BMPointMake();
|
|
435
|
+
if (firstItemPreview._sourceNode) {
|
|
436
|
+
const sourceNodeFrame = BMRectMakeWithNodeFrame(firstItemPreview._sourceNode);
|
|
437
|
+
const center = sourceNodeFrame.center;
|
|
438
|
+
pointerOffset = BMPointMake(center.x - this._position.x, center.y - this._position.y);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Obtain the drag previews for the remaining items
|
|
442
|
+
const previews = [firstItemPreview];
|
|
443
|
+
for (let i = 1; i < this._items.length && previews.length <= BMDragSession._maximumAdditionalDragPreviews; i++) {
|
|
444
|
+
previews.push(this._delegate.dragSessionPreviewForItem(this, this._items[i]));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Create the preview set that will manage the drag previews and play the lifting animation
|
|
448
|
+
const previewSet = new _BMDragPreviewSet().initWithPreviews(previews, {pointerOffset});
|
|
449
|
+
previewSet.beginLiftAtPosition(this._position);
|
|
450
|
+
this._previewSet = previewSet;
|
|
451
|
+
this._dragIndicator = previewSet._dragIndicator;
|
|
452
|
+
this._dragIndicator.setAcceptableItemCount(this._items.length);
|
|
453
|
+
|
|
454
|
+
// Assume that the drag starts out within the source view and notify the delegate accordingly
|
|
455
|
+
this._dropTarget = this._sourceView;
|
|
456
|
+
this._delegate.dragSessionDidEnter?.(this);
|
|
457
|
+
const action = this._delegate.dragSessionDidUpdate?.(this);
|
|
458
|
+
if (action) {
|
|
459
|
+
this._sourceDropAction = action;
|
|
460
|
+
this._updateDropAction();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Register the event that will track the movement of the pointer for this drag session
|
|
464
|
+
window.addEventListener(this._touchIdentifier !== undefined ? 'touchmove' : 'mousemove', this, {capture: YES, passive: NO});
|
|
465
|
+
|
|
466
|
+
// Register the event that will track the end of the gesture
|
|
467
|
+
window.addEventListener(this._touchIdentifier !== undefined ? 'touchend' : 'mouseup', this, {capture: YES, passive: NO});
|
|
468
|
+
if (this._touchIdentifier !== undefined) {
|
|
469
|
+
window.addEventListener('touchcancel', this, {capture: YES, passive: NO});
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
handleEvent(/** @type {MouseEvent | TouchEvent} */ event) {
|
|
474
|
+
if (event.type == 'mousemove' || event.type == 'touchmove') {
|
|
475
|
+
this._dragDidMoveWithEvent(event);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
this._dragDidFinishWithEvent(event);
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Invoked whenever the pointer moves while this drag session is in progress.
|
|
484
|
+
* @param event <MouseEvent or TouchEvent> The event.
|
|
485
|
+
*/
|
|
486
|
+
_dragDidMoveWithEvent(event) {
|
|
487
|
+
event.preventDefault();
|
|
488
|
+
|
|
489
|
+
let position;
|
|
490
|
+
const touchIdentifier = this._touchIdentifier;
|
|
491
|
+
if (touchIdentifier !== undefined) {
|
|
492
|
+
// Verify, when handling touch events, that the pointer assigned to the drag event did change
|
|
493
|
+
event.preventDefault();
|
|
494
|
+
for (var i = 0; i < event.changedTouches.length; i++) {
|
|
495
|
+
if (event.changedTouches[i].identifier == touchIdentifier) {
|
|
496
|
+
position = BMPointMake(
|
|
497
|
+
event.changedTouches[i].clientX,
|
|
498
|
+
event.changedTouches[i].clientY,
|
|
499
|
+
);
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// If the tracked touch did not change, ignore this touchmove event
|
|
505
|
+
if (i == event.changedTouches.length) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
position = BMPointMake(event.clientX, event.clientY);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
this._position = position;
|
|
514
|
+
|
|
515
|
+
// Update the positions of the dragging preview
|
|
516
|
+
this._previewSet.setPosition(position);
|
|
517
|
+
|
|
518
|
+
// Dispatch an update to the source view, and to the current drop target, if any
|
|
519
|
+
// then update the drop action accordingly
|
|
520
|
+
const sourceAction = this._delegate.dragSessionDidUpdate?.(this);
|
|
521
|
+
let targetAction;
|
|
522
|
+
|
|
523
|
+
if (this._dropTarget && this._dropTarget != this._sourceView) {
|
|
524
|
+
const session = this._dropSessions.get(this._dropTarget);
|
|
525
|
+
targetAction = session._delegate.dropSessionDidUpdate?.(session);
|
|
526
|
+
if (targetAction) {
|
|
527
|
+
// If the target action is delete, the source delegate must be able to specify
|
|
528
|
+
// a transfer of kind move
|
|
529
|
+
if (targetAction._action == BMDropSessionActionKind.Delete) {
|
|
530
|
+
const transferKind = this._delegate.dragSessionTransferKind(this);
|
|
531
|
+
if (transferKind != BMDragTransferKind.Move) {
|
|
532
|
+
targetAction = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
session._dropAction = targetAction;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (sourceAction) {
|
|
541
|
+
this._sourceDropAction = sourceAction;
|
|
542
|
+
}
|
|
543
|
+
if (targetAction) {
|
|
544
|
+
this._targetDropAction = targetAction;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (sourceAction || targetAction) {
|
|
548
|
+
this._updateDropAction();
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Invoked when the pointer is released or cancelled while this drag session is in progress.
|
|
554
|
+
* @param event <MouseEvent or TouchEvent> The event.
|
|
555
|
+
* @return <Promise<void>> A promise that resolves when all associated animations finish.
|
|
556
|
+
*/
|
|
557
|
+
async _dragDidFinishWithEvent(event) {
|
|
558
|
+
event.preventDefault();
|
|
559
|
+
|
|
560
|
+
this._action = this._dropAction;
|
|
561
|
+
|
|
562
|
+
// Instruct the delegates that the session is about to end
|
|
563
|
+
this._delegate.dragSessionWillFinish?.(this);
|
|
564
|
+
|
|
565
|
+
for (const [view, session] of this._dropSessions.entries()) {
|
|
566
|
+
if (view == this._sourceView) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
session._delegate.dropSessionWillFinish?.(this);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Clear out the event listeners
|
|
573
|
+
window.removeEventListener(this._touchIdentifier !== undefined ? 'touchmove' : 'mousemove', this, {capture: YES, passive: NO});
|
|
574
|
+
window.removeEventListener(this._touchIdentifier !== undefined ? 'touchend' : 'mouseup', this, {capture: YES, passive: NO});
|
|
575
|
+
if (this._touchIdentifier !== undefined) {
|
|
576
|
+
window.removeEventListener('touchcancel', this, {capture: YES, passive: NO});
|
|
577
|
+
}
|
|
578
|
+
this._releaseDropTargetHandlers();
|
|
579
|
+
|
|
580
|
+
// If there is a drop target and its action is not ignore, instruct the delegate to perform the action
|
|
581
|
+
if (this._dropTarget && this._dropTarget != this._sourceView) {
|
|
582
|
+
const session = this._dropSessions.get(this._dropTarget);
|
|
583
|
+
if ((session._dropAction?._action ?? BMDropSessionActionKind.Ignore) != BMDropSessionActionKind.Ignore) {
|
|
584
|
+
// Determine whether the delegate wants to handle the drop animation for the acceptable items
|
|
585
|
+
let acceptableItems = this._items;
|
|
586
|
+
if (session._dropAction._action == BMDropSessionActionKind.AcceptPartially) {
|
|
587
|
+
acceptableItems = session._dropAction._acceptableItems ?? acceptableItems;
|
|
588
|
+
}
|
|
589
|
+
else if (session._dropAction._action == BMDropSessionActionKind.Reject) {
|
|
590
|
+
acceptableItems = [];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// A transfer only occurs if the action is to accept or accept partially
|
|
594
|
+
const isTransfer = [BMDropSessionActionKind.Accept, BMDropSessionActionKind.AcceptPartially].includes(session._dropAction?._action);
|
|
595
|
+
|
|
596
|
+
// Request the transfer kind from both delegates and set it to move if both of the specify a transfer kind of move,
|
|
597
|
+
// defaulting to copy if either of them provides a different response
|
|
598
|
+
let transferKind = BMDragTransferKind.Copy;
|
|
599
|
+
if (isTransfer) {
|
|
600
|
+
const sourceTransferKind = this._delegate.dragSessionTransferKind?.(this) ?? BMDragTransferKind.Copy;
|
|
601
|
+
const targetTransferKind = session._delegate.dropSessionTransferKind?.(session) ?? BMDragTransferKind.Copy;
|
|
602
|
+
|
|
603
|
+
if (sourceTransferKind == BMDragTransferKind.Move && targetTransferKind == BMDragTransferKind.Move) {
|
|
604
|
+
transferKind = BMDragTransferKind.Move;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
this._transferKind = transferKind;
|
|
609
|
+
session._transferKind = transferKind;
|
|
610
|
+
|
|
611
|
+
const transferItems = acceptableItems;
|
|
612
|
+
|
|
613
|
+
acceptableItems = acceptableItems.filter(i => this._previewSet._dragPreviews.get(i));
|
|
614
|
+
let requiresTargetDropPreviews = NO;
|
|
615
|
+
|
|
616
|
+
// Custom animations are only supported for accepted items
|
|
617
|
+
if (isTransfer) {
|
|
618
|
+
requiresTargetDropPreviews = session._delegate.dropSessionRequiresCustomDropAnimationForItems?.(session, acceptableItems.slice());
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
let requiresSourceDropPreviews = NO;
|
|
622
|
+
const unacceptableItems = [...this._previewSet._dragPreviews.keys()].filter(i => !acceptableItems.includes(i));
|
|
623
|
+
// If there are still items that are not accepted, ask the drag delegate if it performs a custom animation
|
|
624
|
+
if (transferItems.length != this._items.length && unacceptableItems.length) {
|
|
625
|
+
requiresSourceDropPreviews = this._delegate.dragSessionRequiresCustomDropAnimationForItems?.(this, unacceptableItems.slice());
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
session._dropItems = transferItems;
|
|
629
|
+
|
|
630
|
+
// If a custom animation is required, create the drop previews
|
|
631
|
+
if (requiresTargetDropPreviews) {
|
|
632
|
+
session._dropPreviews = acceptableItems.map(i => {
|
|
633
|
+
const dragPreview = this._previewSet._dragPreviews.get(i);
|
|
634
|
+
dragPreview._dropHandled = YES;
|
|
635
|
+
return new BMDropPreview()._initWithDragPreview(dragPreview, {forItem: i});
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// If the source requires a custom animation, create the drop previews
|
|
640
|
+
if (requiresSourceDropPreviews) {
|
|
641
|
+
this._dropPreviews = unacceptableItems.map(i => {
|
|
642
|
+
const dragPreview = this._previewSet._dragPreviews.get(i);
|
|
643
|
+
dragPreview._dropHandled = YES;
|
|
644
|
+
return new BMDropPreview()._initWithDragPreview(dragPreview, {forItem: i});
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
this._delegate.dragSessionAnimateDropWithPreviews?.(this, this._dropPreviews);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// If both delegates request a move, instruct the source delegate to remove the items that will be transferred
|
|
651
|
+
if (transferKind == BMDragTransferKind.Move && transferItems.length) {
|
|
652
|
+
this._delegate.dragSessionPerformMoveForItems(this, transferItems);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
session._delegate.dropSessionPerformDrop(session);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
if (this._dropAction._action == BMDragSessionActionKind.Delete) {
|
|
660
|
+
// If the source view has specified an action to delete, instruct it to delete the items now
|
|
661
|
+
this._delegate.dragSessionPerformDelete(this);
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
// Otherwise ask the delegate if it wants to handle the drop animation
|
|
665
|
+
const items = [...this._previewSet._dragPreviews.keys()];
|
|
666
|
+
const requiresDropPreviews = this._delegate.dragSessionRequiresCustomDropAnimationForItems?.(this, items.slice());
|
|
667
|
+
|
|
668
|
+
if (requiresDropPreviews) {
|
|
669
|
+
this._delegate.dragSessionAnimateDropWithPreviews?.(this, this._dropPreviews);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Perform the drop animation and detach the previews
|
|
675
|
+
this._previewSet.performDrop().then(() => {
|
|
676
|
+
// At the end of the associated animations, let the delegates know that the session
|
|
677
|
+
// has completely finished
|
|
678
|
+
this._delegate.dragSessionDidFinish?.(this);
|
|
679
|
+
|
|
680
|
+
for (const [view, session] of this._dropSessions.entries()) {
|
|
681
|
+
if (view == this._sourceView) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
session._delegate.dropSessionDidFinish?.(this);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
},
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Sets up the event handlers that are used to determine when the drag moves over one
|
|
691
|
+
* of the valid drop targets.
|
|
692
|
+
*/
|
|
693
|
+
_initDropTargetHandlers() {
|
|
694
|
+
if (this._touchIdentifier) {
|
|
695
|
+
// For touch drag session, mouseover and mouseout cannot be used as there are no mouse events or equivalents for touch events
|
|
696
|
+
// Instead, throughout the drag operation, the pointer's position is converted into the topmost node and if that node
|
|
697
|
+
// is a child of any collection view, that collection view becomes the drop target
|
|
698
|
+
// This handler is installed globally and will affect all possible drop targets
|
|
699
|
+
this._touchDropTargetHandler = event => {
|
|
700
|
+
let clientX, clientY;
|
|
701
|
+
for (var i = 0; i < event.changedTouches.length; i++) {
|
|
702
|
+
if (event.changedTouches[i].identifier == this._touchIdentifier) {
|
|
703
|
+
clientX = event.changedTouches[i].clientX;
|
|
704
|
+
clientY = event.changedTouches[i].clientY;
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// If the tracked touch did not change, ignore this touchmove event
|
|
710
|
+
if (i == event.changedTouches.length) return NO;
|
|
711
|
+
|
|
712
|
+
// Get the node corresponding to the current point
|
|
713
|
+
let node = document.elementFromPoint(clientX, clientY);
|
|
714
|
+
|
|
715
|
+
// Check if that node belongs to one of the drop targets
|
|
716
|
+
let target;
|
|
717
|
+
if (node) {
|
|
718
|
+
for (const view of this._dropSessions.keys()) {
|
|
719
|
+
if (view.node.contains(node)) {
|
|
720
|
+
target = view;
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Update the drop target based on what was discovered
|
|
727
|
+
if (target != this._dropTarget) {
|
|
728
|
+
this._setDropTarget(target);
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
window.addEventListener('touchmove', this._touchDropTargetHandler, {capture: YES, passive: NO});
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
// For mouse drag sessions, use "mouseenter" and "mouseleave" to determine when the drop target changes
|
|
736
|
+
for (const [view, session] of this._dropSessions.entries()) {
|
|
737
|
+
session._eventHandlers = {
|
|
738
|
+
mouseenter: event => {
|
|
739
|
+
this._setDropTarget(view);
|
|
740
|
+
|
|
741
|
+
event.preventDefault();
|
|
742
|
+
event.stopPropagation();
|
|
743
|
+
return NO;
|
|
744
|
+
},
|
|
745
|
+
|
|
746
|
+
mouseleave: event => {
|
|
747
|
+
this._setDropTarget(undefined);
|
|
748
|
+
|
|
749
|
+
event.preventDefault();
|
|
750
|
+
event.stopPropagation();
|
|
751
|
+
return NO;
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
for (const key in session._eventHandlers) {
|
|
756
|
+
view.node.addEventListener(key, session._eventHandlers[key]);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Removes the event handlers used to determine the drop target that have been set up for this drag session.
|
|
764
|
+
*/
|
|
765
|
+
_releaseDropTargetHandlers() {
|
|
766
|
+
if (this._touchIdentifier) {
|
|
767
|
+
window.removeEventListener('touchmove', this._touchDropTargetHandler, {capture: YES, passive: NO});
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
for (const [view, session] of this._dropSessions.entries()) {
|
|
771
|
+
for (const key in session._eventHandlers) {
|
|
772
|
+
view.node.removeEventListener(key, session._eventHandlers[key]);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* An identifier for the timeout registered to update the item previews after the
|
|
780
|
+
* drag gesture moves over a new drop target.
|
|
781
|
+
*/
|
|
782
|
+
_itemPreviewsUpdateIdentifier: undefined, // <Number, nullable>
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Updates the drop target, invoking the appropriate method on the delegate objects.
|
|
786
|
+
* @param target <BMView, nullable> The current drop target, or `undefined` if the drag gesture
|
|
787
|
+
* is not currently over any drop target.
|
|
788
|
+
*/
|
|
789
|
+
_setDropTarget(target) {
|
|
790
|
+
if (target == this._dropTarget) {
|
|
791
|
+
// Don't take any action if the target doesn't actually change
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Let the delegate of the current drop target know that the drag exited its bounds
|
|
796
|
+
if (this._dropTarget) {
|
|
797
|
+
if (this._dropTarget == this._sourceView) {
|
|
798
|
+
this._delegate.dragSessionDidExit?.(this);
|
|
799
|
+
this._targetDropAction = undefined;
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
const session = this._dropSessions.get(this._dropTarget);
|
|
803
|
+
session._delegate.dropSessionDidExit?.(session);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Let the delegate of the new drop target know that the drag entered its bounds
|
|
808
|
+
// Also dispatch a movement update to the delegate and obtain the drop outcome
|
|
809
|
+
this._dropTarget = target;
|
|
810
|
+
if (target) {
|
|
811
|
+
if (target == this._sourceView) {
|
|
812
|
+
this._delegate.dragSessionDidEnter?.(this);
|
|
813
|
+
|
|
814
|
+
const action = this._delegate.dragSessionDidUpdate?.(this);
|
|
815
|
+
if (action) {
|
|
816
|
+
this._sourceDropAction = action;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
const session = this._dropSessions.get(target);
|
|
821
|
+
session._delegate.dropSessionDidEnter?.(session);
|
|
822
|
+
|
|
823
|
+
let action = session._delegate.dropSessionDidUpdate?.(this) ?? BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
|
|
824
|
+
|
|
825
|
+
// If the target action is delete, the source delegate must be able to specify
|
|
826
|
+
// a transfer of kind move
|
|
827
|
+
if (action._action == BMDropSessionActionKind.Delete) {
|
|
828
|
+
const transferKind = this._delegate.dragSessionTransferKind(this);
|
|
829
|
+
if (transferKind != BMDragTransferKind.Move) {
|
|
830
|
+
action = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
this._targetDropAction = action;
|
|
835
|
+
session._dropAction = action;
|
|
836
|
+
|
|
837
|
+
const sourceAction = this._delegate.dragSessionDidUpdate?.(this);
|
|
838
|
+
if (action) {
|
|
839
|
+
this._sourceDropAction = sourceAction;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
this._updateDropAction();
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
this._targetDropAction = undefined;
|
|
847
|
+
this._updateDropAction();
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// If an item preview update was already registered, don't take any further action
|
|
851
|
+
if (this._itemPreviewsUpdateIdentifier) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Else register an item preview update after a short delay to account for situations where
|
|
856
|
+
// the gesture immediately exits and enters a new drop target
|
|
857
|
+
this._itemPreviewsUpdateIdentifier = setTimeout(() => {
|
|
858
|
+
this._itemPreviewsUpdateIdentifier = undefined;
|
|
859
|
+
|
|
860
|
+
const newPreviews = new Map();
|
|
861
|
+
if (this._dropTarget && this._dropTarget != this._sourceView) {
|
|
862
|
+
// If there is a valid drop target, request item previews and apply them
|
|
863
|
+
const session = this._dropSessions.get(this._dropTarget);
|
|
864
|
+
for (const item of this._previewSet._baseDragPreviews.keys()) {
|
|
865
|
+
const preview = session._delegate.dropSessionPreviewForItem?.(session, item);
|
|
866
|
+
newPreviews.set(item, preview);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
// Otherwise reset the previews
|
|
871
|
+
for (const item of this._previewSet._baseDragPreviews.keys()) {
|
|
872
|
+
newPreviews.set(item, undefined);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
this._previewSet.updatePreviewsWithMap(newPreviews);
|
|
877
|
+
}, 16);
|
|
878
|
+
},
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Updates the drop action displayed by this drag session, based on the current
|
|
882
|
+
* source and target drop actions.
|
|
883
|
+
*/
|
|
884
|
+
_updateDropAction() {
|
|
885
|
+
let action;
|
|
886
|
+
// If the target specifies a drop action that is not ignore, display it
|
|
887
|
+
if (this._targetDropAction && this._targetDropAction._action != BMDropSessionActionKind.Ignore) {
|
|
888
|
+
action = this._targetDropAction;
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
action = this._sourceDropAction ?? BMDragSessionAction.actionWithKind(BMDragSessionActionKind.Ignore);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (action == this._dropAction) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
this._dropAction = action;
|
|
899
|
+
|
|
900
|
+
if (action._action == BMDropSessionActionKind.AcceptPartially && action._acceptableItems) {
|
|
901
|
+
if (action._acceptableItems.length == 0) {
|
|
902
|
+
// If the action is specified as accept partially but all items are rejected, interpret
|
|
903
|
+
// the action as rejecting the drop
|
|
904
|
+
action = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Reject, {
|
|
905
|
+
message: action._message,
|
|
906
|
+
messageHTML: action._messageHTML,
|
|
907
|
+
});
|
|
908
|
+
this._dropAction = action;
|
|
909
|
+
|
|
910
|
+
this._dragIndicator.setAcceptableItemCount(this._items.length);
|
|
911
|
+
this._previewSet.setRejectedItems(undefined);
|
|
912
|
+
}
|
|
913
|
+
else if (action._acceptableItems.length == this._items.length) {
|
|
914
|
+
// If the action is specified as accept partially but all items are accepted, interpret
|
|
915
|
+
// the action as accepting the drop
|
|
916
|
+
action = BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Accept, {
|
|
917
|
+
message: action._message,
|
|
918
|
+
messageHTML: action._messageHTML,
|
|
919
|
+
});
|
|
920
|
+
this._dropAction = action;
|
|
921
|
+
|
|
922
|
+
this._dragIndicator.setAcceptableItemCount(this._items.length);
|
|
923
|
+
this._previewSet.setRejectedItems(undefined);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
this._dragIndicator.setAcceptableItemCount(action._acceptableItems.length);
|
|
927
|
+
|
|
928
|
+
// If all items with previews are rejected, request and display an additional preview for
|
|
929
|
+
// the first acceptable item
|
|
930
|
+
let requiresAdditionalPreview = YES;
|
|
931
|
+
const rejectedItems = this._items.filter((item, index) => {
|
|
932
|
+
const rejected = !action._acceptableItems.includes(item);
|
|
933
|
+
|
|
934
|
+
if (!rejected && index < BMDragSession._maximumAdditionalDragPreviews + 1) {
|
|
935
|
+
requiresAdditionalPreview = NO;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return rejected;
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
// If an additional preview is required, obtain and display it, otherwise clear any current additional preview
|
|
942
|
+
if (requiresAdditionalPreview) {
|
|
943
|
+
const item = action._acceptableItems[0];
|
|
944
|
+
|
|
945
|
+
if (this._previewSet._additionalPreview?._dragItem != item) {
|
|
946
|
+
// If the preview set is not already displaying an additional preview for this item, update it
|
|
947
|
+
if (!this._dropTarget || this._dropTarget == this._sourceView) {
|
|
948
|
+
const preview = this._delegate.dragSessionPreviewForItem(this, item);
|
|
949
|
+
this._previewSet.setAdditionalPreview(preview);
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
const session = this._dropSessions.get(this._dropTarget);
|
|
953
|
+
let preview = session._delegate.dropSessionPreviewForItem?.(session, item);
|
|
954
|
+
preview ??= this._delegate.dragSessionPreviewForItem(this, item);
|
|
955
|
+
this._previewSet.setAdditionalPreview(preview);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
this._previewSet.setAdditionalPreview(undefined);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
this._previewSet.setRejectedItems(rejectedItems);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
this._dragIndicator.setAcceptableItemCount(this._items.length);
|
|
968
|
+
this._previewSet.setRejectedItems(undefined);
|
|
969
|
+
this._previewSet.setAdditionalPreview(undefined);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
this._dragIndicator.setAction(action);
|
|
973
|
+
},
|
|
974
|
+
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Starts a drag and drop session from the specified mouse or touch event.
|
|
979
|
+
* using a delegate to supply information about the items that are being transferred.
|
|
980
|
+
* @param event <MouseEvent or TouchEvent> The event starting the drag.
|
|
981
|
+
* {
|
|
982
|
+
* @param dragDelegate <BMDragDelegate> The delegate object providing information about the
|
|
983
|
+
* items being dragged.
|
|
984
|
+
* @param view <BMView> The view initiating this drag session.
|
|
985
|
+
* @param touchIdentifier <Number, nullable> If the event is a touch event, the identifier of the
|
|
986
|
+
* touch that should be tracked for the drag session.
|
|
987
|
+
* If not specified, the first touch will be used instead.
|
|
988
|
+
* }
|
|
989
|
+
*/
|
|
990
|
+
BMDragSession._beginDragWithEvent = function (event, args) {
|
|
991
|
+
// Create and initialize a drag session for the specified event
|
|
992
|
+
// The drag session object will handle the events after creation
|
|
993
|
+
const session = new BMDragSession()._initWithEvent(event, args);
|
|
994
|
+
session._beginDrag();
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
// The maximum number of additional drag previews for multi-item drag sessions that can be displayed
|
|
998
|
+
// in addition to the drag preview of the first item.
|
|
999
|
+
BMDragSession._maximumAdditionalDragPreviews = 10; // <Number>
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Sets the maximum number of additional drag previews for multi-item drag sessions that can be displayed
|
|
1003
|
+
* in addition to the drag preview of the first item. Updating this value only affects drag sessions
|
|
1004
|
+
* started after this method returns.
|
|
1005
|
+
* @param max <Number> The new maximum number of previews to display.
|
|
1006
|
+
*/
|
|
1007
|
+
BMDragSession.setMaximumAdditionalDragPreviews = function (max) {
|
|
1008
|
+
BMDragSession._maximumAdditionalDragPreviews = max;
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* A map containing views that are drop targets as keys and their associated drop delegate objects as values.
|
|
1013
|
+
*/
|
|
1014
|
+
BMDragSession._dropTargets = new Map();
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Registers a view as a potential drop target for future drag sessions, using the specified delegate
|
|
1018
|
+
* to handle updates to the associated drop sessions and the drop actions.
|
|
1019
|
+
*
|
|
1020
|
+
* If the view is already registered as a drop target, this will replace the delegate handling future
|
|
1021
|
+
* drop sessions for the view with the specified object.
|
|
1022
|
+
* @param target <BMView> The view that will act as a drop target.
|
|
1023
|
+
* {
|
|
1024
|
+
* @param delegate <BMDropDelegate> The delegate that will handle actions and events from the drop
|
|
1025
|
+
* session on behalf of the target view.
|
|
1026
|
+
* }
|
|
1027
|
+
*/
|
|
1028
|
+
BMDragSession.registerDropTarget = function (target, args) {
|
|
1029
|
+
this._dropTargets.set(target, args.delegate);
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Unregisters a view as a potential drop target for future drag sessions. This has no effect on any
|
|
1034
|
+
* in-progress drop sessions which will continue using the previously registered delegate to handle
|
|
1035
|
+
* events and actions.
|
|
1036
|
+
*
|
|
1037
|
+
* This method has no effect if the view is not registered as a drop target.
|
|
1038
|
+
* @param target <BMView> The view that was previously registered as a drop target.
|
|
1039
|
+
*/
|
|
1040
|
+
BMDragSession.unregisterDropTarget = function (target) {
|
|
1041
|
+
this._dropTargets.delete(target);
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
// @endtype
|
|
1045
|
+
|
|
1046
|
+
// @type BMDropSession
|
|
1047
|
+
|
|
1048
|
+
function BMDropSession() {} // <constructor>
|
|
1049
|
+
|
|
1050
|
+
BMDropSession.prototype = {
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* The drag session managing the drag gesture for which this drop session was created.
|
|
1054
|
+
*/
|
|
1055
|
+
_dragSession: undefined, // <BMDragSession>
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* The drop delegate specifying the behaviour of the drop over this drop target.
|
|
1059
|
+
*/
|
|
1060
|
+
_delegate: undefined, // <BMDropDelegate>
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* A dictionary of event handlers that have been attached to the drop target view for which this
|
|
1064
|
+
* drop session was created.
|
|
1065
|
+
*/
|
|
1066
|
+
_eventHandlers: undefined, // <Dictionary<unknown>>
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* The current drop action.
|
|
1070
|
+
*/
|
|
1071
|
+
_dropAction: BMDropSessionAction.actionWithKind(BMDropSessionActionKind.Ignore), // <BMDropSessionAction>
|
|
1072
|
+
|
|
1073
|
+
get dropAction() {
|
|
1074
|
+
return this._dropAction;
|
|
1075
|
+
},
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* An array of drop previews that can be used to customize the drop animation. This property is only
|
|
1079
|
+
* initialized at the end of accepted drop sessions if the delegate object implemented the
|
|
1080
|
+
* `dropSessionRequiresCustomDropAnimationForItems` method and returned `YES` from it.
|
|
1081
|
+
*/
|
|
1082
|
+
_dropPreviews: undefined, // <[BMDropPreview], nullable>
|
|
1083
|
+
|
|
1084
|
+
get dropPreviews() {
|
|
1085
|
+
return this._dropPreviews;
|
|
1086
|
+
},
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* The kind of transfer that will be performed. Only set before the `dropSessionPerformDrop` delegate
|
|
1090
|
+
* method is invoked if the outcome of this drop session is a transfer of items into the target view.
|
|
1091
|
+
*/
|
|
1092
|
+
_transferKind: undefined, // <BMDragTransferKind, nullable>
|
|
1093
|
+
|
|
1094
|
+
get transferKind() {
|
|
1095
|
+
return this._transferKind;
|
|
1096
|
+
},
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Initializes this drop session with the specified drag session and the drop delegate.
|
|
1100
|
+
* @param session <BMDragSession> The drag session associated with this drop session
|
|
1101
|
+
* {
|
|
1102
|
+
* @param delegate <BMDropDelegate> The drop delegate used to determine the outcome of the drop.
|
|
1103
|
+
* }
|
|
1104
|
+
* @returns <BMDropSession> This drop session.
|
|
1105
|
+
*/
|
|
1106
|
+
_initWithDragSession(session, {delegate}) {
|
|
1107
|
+
this._dragSession = session;
|
|
1108
|
+
this._delegate = delegate;
|
|
1109
|
+
return this;
|
|
1110
|
+
},
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* The current position of the drag and drop gesture.
|
|
1114
|
+
*/
|
|
1115
|
+
get position() { // <BMPoint>
|
|
1116
|
+
return this._dragSession.position;
|
|
1117
|
+
},
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* The items being transferred in the drag and drop gesture.
|
|
1121
|
+
*/
|
|
1122
|
+
get items() { // <[BMDragItem]>
|
|
1123
|
+
return this._dragSession.items;
|
|
1124
|
+
},
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* The items that have been accepted through this drop session. This property is only initialized
|
|
1128
|
+
* before the `dropSessionPerformDrop` delegate method is invoked.
|
|
1129
|
+
*/
|
|
1130
|
+
_dropItems: undefined, // <[BMDragItem], nullable>
|
|
1131
|
+
|
|
1132
|
+
get dropItems() {
|
|
1133
|
+
return this._dropItems;
|
|
1134
|
+
},
|
|
1135
|
+
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Creates and returns a drop session initialized with the specified drag session and the drop delegate.
|
|
1140
|
+
* @param session <BMDragSession> The drag session associated with the drop session
|
|
1141
|
+
* {
|
|
1142
|
+
* @param delegate <BMDropDelegate> The drop delegate used to determine the outcome of the drop.
|
|
1143
|
+
* }
|
|
1144
|
+
* @returns <BMDropSession> A drop session.
|
|
1145
|
+
*/
|
|
1146
|
+
BMDropSession._sessionForDragSession = function (session, args) {
|
|
1147
|
+
return new BMDropSession()._initWithDragSession(session, args);
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
// @endtype
|