in packages/react/src/utilities/dragdrop/DragDropHelper.tsx [57:247]
public subscribe(
root: HTMLElement,
events: EventGroup,
dragDropOptions: IDragDropOptions,
): {
key: string;
dispose(): void;
} {
if (!this._initialized) {
this._events = new EventGroup(this);
const doc = getDocument();
// clear drag data when mouse up, use capture event to ensure it will be run
if (doc) {
this._events.on(doc.body, 'mouseup', this._onMouseUp.bind(this), true);
this._events.on(doc, 'mouseup', this._onDocumentMouseUp.bind(this), true);
}
this._initialized = true;
}
const { key = `${++this._lastId}` } = dragDropOptions;
const handlers: {
callback: (context: IDragDropContext, event?: any) => void;
eventName: string;
}[] = [];
let onDragStart: (event: DragEvent) => void;
let onDragLeave: (event: DragEvent) => void;
let onDragEnter: (event: DragEvent) => void;
let onDragEnd: (event: DragEvent) => void;
let onDrop: (event: DragEvent) => void;
let onDragOver: (event: DragEvent) => void;
let onMouseDown: (event: MouseEvent) => void;
let isDraggable: boolean;
let isDroppable: boolean;
let activeTarget: {
target: IDragDropTarget;
dispose: () => void;
};
if (dragDropOptions && root) {
const { eventMap, context, updateDropState } = dragDropOptions;
const dragDropTarget: IDragDropTarget = {
root: root,
options: dragDropOptions,
key: key,
};
isDraggable = this._isDraggable(dragDropTarget);
isDroppable = this._isDroppable(dragDropTarget);
if (isDraggable || isDroppable) {
if (eventMap) {
for (const event of eventMap) {
const handler = {
callback: event.callback.bind(null, context),
eventName: event.eventName,
};
handlers.push(handler);
this._events.on(root, handler.eventName, handler.callback);
}
}
}
if (isDroppable) {
// If the target is droppable, wire up global event listeners to track drop-related events.
onDragLeave = (event: DragEvent) => {
if (!(event as IDragDropEvent).isHandled) {
(event as IDragDropEvent).isHandled = true;
this._dragEnterCounts[key]--;
if (this._dragEnterCounts[key] === 0) {
updateDropState(false /* isDropping */, event);
}
}
};
onDragEnter = (event: DragEvent) => {
event.preventDefault(); // needed for IE
if (!(event as IDragDropEvent).isHandled) {
(event as IDragDropEvent).isHandled = true;
this._dragEnterCounts[key]++;
if (this._dragEnterCounts[key] === 1) {
updateDropState(true /* isDropping */, event);
}
}
};
onDragEnd = (event: DragEvent) => {
this._dragEnterCounts[key] = 0;
updateDropState(false /* isDropping */, event);
};
onDrop = (event: DragEvent) => {
this._dragEnterCounts[key] = 0;
updateDropState(false /* isDropping */, event);
if (dragDropOptions.onDrop) {
dragDropOptions.onDrop(dragDropOptions.context.data, event);
}
};
onDragOver = (event: DragEvent) => {
event.preventDefault();
if (dragDropOptions.onDragOver) {
dragDropOptions.onDragOver(dragDropOptions.context.data, event);
}
};
this._dragEnterCounts[key] = 0;
// dragenter and dragleave will be fired when hover to the child element
// but we only want to change state when enter or leave the current element
// use the count to ensure it.
events.on(root, 'dragenter', onDragEnter);
events.on(root, 'dragleave', onDragLeave);
events.on(root, 'dragend', onDragEnd);
events.on(root, 'drop', onDrop);
events.on(root, 'dragover', onDragOver);
}
if (isDraggable) {
// If the target is draggable, wire up local event listeners for mouse events.
onMouseDown = this._onMouseDown.bind(this, dragDropTarget);
onDragEnd = this._onDragEnd.bind(this, dragDropTarget);
// We need to add in data so that on Firefox we show the ghost element when dragging
onDragStart = (event: DragEvent) => {
const options = dragDropOptions;
if (options && options.onDragStart) {
options.onDragStart(options.context.data, options.context.index, this._selection.getSelection(), event);
}
this._isDragging = true;
if (event.dataTransfer) {
event.dataTransfer.setData('id', root.id);
}
};
events.on(root, 'dragstart', onDragStart);
events.on(root, 'mousedown', onMouseDown);
events.on(root, 'dragend', onDragEnd);
}
activeTarget = {
target: dragDropTarget,
dispose: () => {
if (this._activeTargets[key] === activeTarget) {
delete this._activeTargets[key];
}
if (root) {
for (const handler of handlers) {
this._events.off(root, handler.eventName, handler.callback);
}
if (isDroppable) {
events.off(root, 'dragenter', onDragEnter);
events.off(root, 'dragleave', onDragLeave);
events.off(root, 'dragend', onDragEnd);
events.off(root, 'dragover', onDragOver);
events.off(root, 'drop', onDrop);
}
if (isDraggable) {
events.off(root, 'dragstart', onDragStart);
events.off(root, 'mousedown', onMouseDown);
events.off(root, 'dragend', onDragEnd);
}
}
},
};
this._activeTargets[key] = activeTarget;
}
return {
key: key,
dispose: () => {
if (activeTarget) {
activeTarget.dispose();
}
},
};
}