UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

304 lines • 14.5 kB
import * as ReactDOM from 'react-dom'; import { EventGroup } from '../../Utilities'; var MOUSEDOWN_PRIMARY_BUTTON = 0; // for mouse down event we are using ev.button property, 0 means left button var MOUSEMOVE_PRIMARY_BUTTON = 1; // for mouse move event we are using ev.buttons property, 1 means left button var DragDropHelper = /** @class */ (function () { function DragDropHelper(params) { this._selection = params.selection; this._dragEnterCounts = {}; this._activeTargets = {}; this._lastId = 0; this._events = new EventGroup(this); // clear drag data when mouse up, use capture event to ensure it will be run this._events.on(document.body, 'mouseup', this._onMouseUp.bind(this), true); this._events.on(document, 'mouseup', this._onDocumentMouseUp.bind(this), true); } DragDropHelper.prototype.dispose = function () { this._events.dispose(); }; DragDropHelper.prototype.subscribe = function (root, events, dragDropOptions) { var _this = this; var _a = dragDropOptions.key, key = _a === void 0 ? "" + ++this._lastId : _a; var handlers = []; var onDragStart; var onDragLeave; var onDragEnter; var onDragEnd; var onDrop; var onDragOver; var onMouseDown; var isDraggable; var isDroppable; var activeTarget; if (dragDropOptions && root) { var eventMap = dragDropOptions.eventMap, context = dragDropOptions.context, updateDropState_1 = dragDropOptions.updateDropState; var dragDropTarget = { root: root, options: dragDropOptions, key: key }; isDraggable = this._isDraggable(dragDropTarget); isDroppable = this._isDroppable(dragDropTarget); if (isDraggable || isDroppable) { if (eventMap) { for (var _i = 0, eventMap_1 = eventMap; _i < eventMap_1.length; _i++) { var event_1 = eventMap_1[_i]; var handler = { callback: event_1.callback.bind(null, context), eventName: event_1.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 = function (event) { if (!event.isHandled) { event.isHandled = true; _this._dragEnterCounts[key]--; if (_this._dragEnterCounts[key] === 0) { updateDropState_1(false /* isDropping */, event); } } }; onDragEnter = function (event) { event.preventDefault(); // needed for IE if (!event.isHandled) { event.isHandled = true; _this._dragEnterCounts[key]++; if (_this._dragEnterCounts[key] === 1) { updateDropState_1(true /* isDropping */, event); } } }; onDragEnd = function (event) { _this._dragEnterCounts[key] = 0; updateDropState_1(false /* isDropping */, event); }; onDrop = function (event) { _this._dragEnterCounts[key] = 0; updateDropState_1(false /* isDropping */, event); if (dragDropOptions.onDrop) { dragDropOptions.onDrop(dragDropOptions.context.data, event); } }; onDragOver = function (event) { 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 = function (event) { var options = _this._dragData.dragTarget.options; if (options && options.onDragStart) { options.onDragStart(options.context.data, options.context.index, _this._selection.getSelection(), event); } _this._isDragging = true; 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: function () { if (_this._activeTargets[key] === activeTarget) { delete _this._activeTargets[key]; } if (root) { for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { var handler = handlers_1[_i]; _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: function () { if (activeTarget) { activeTarget.dispose(); } } }; }; DragDropHelper.prototype.unsubscribe = function (root, key) { var activeTarget = this._activeTargets[key]; if (activeTarget) { activeTarget.dispose(); } }; DragDropHelper.prototype._onDragEnd = function (target, event) { var options = target.options; if (options.onDragEnd) { options.onDragEnd(options.context.data, event); } }; /** * clear drag data when mouse up on body */ DragDropHelper.prototype._onMouseUp = function (event) { this._isDragging = false; if (this._dragData) { for (var _i = 0, _a = Object.keys(this._activeTargets); _i < _a.length; _i++) { var key = _a[_i]; var activeTarget = this._activeTargets[key]; if (activeTarget.target.root) { this._events.off(activeTarget.target.root, 'mousemove'); this._events.off(activeTarget.target.root, 'mouseleave'); } } if (this._dragData.dropTarget) { // raise dragleave event to let dropTarget know it need to remove dropping style EventGroup.raise(this._dragData.dropTarget.root, 'dragleave'); EventGroup.raise(this._dragData.dropTarget.root, 'drop'); } } this._dragData = null; }; /** * clear drag data when mouse up outside of the document */ DragDropHelper.prototype._onDocumentMouseUp = function (event) { if (event.target === document.documentElement) { this._onMouseUp(event); } }; /** * when mouse move over a new drop target while dragging some items, * fire dragleave on the old target and fire dragenter to the new target * The target will handle style change on dragenter and dragleave events. */ DragDropHelper.prototype._onMouseMove = function (target, event) { var // use buttons property here since ev.button in some edge case is not updating well during the move. // but firefox doesn't support it, so we set the default value when it is not defined. _a = event.buttons, // use buttons property here since ev.button in some edge case is not updating well during the move. // but firefox doesn't support it, so we set the default value when it is not defined. buttons = _a === void 0 ? MOUSEMOVE_PRIMARY_BUTTON : _a; if (this._dragData && buttons !== MOUSEMOVE_PRIMARY_BUTTON) { // cancel mouse down event and return early when the primary button is not pressed this._onMouseUp(event); return; } var root = target.root, key = target.key; if (this._isDragging) { if (this._isDroppable(target)) { // we can have nested drop targets in the DOM, like a folder inside a group. In that case, when we drag into // the inner target (folder), we first set dropTarget to the inner element. But the same event is bubbled to the // outer target too, and we need to prevent the outer one from taking over. // So, check if the last dropTarget is not a child of the current. if (this._dragData) { if (this._dragData.dropTarget && this._dragData.dropTarget.key !== key && !this._isChild(root, this._dragData.dropTarget.root)) { if (this._dragEnterCounts[this._dragData.dropTarget.key] > 0) { EventGroup.raise(this._dragData.dropTarget.root, 'dragleave'); EventGroup.raise(root, 'dragenter'); this._dragData.dropTarget = target; } } } } } }; /** * when mouse leave a target while dragging some items, fire dragleave to the target */ DragDropHelper.prototype._onMouseLeave = function (target, event) { if (this._isDragging) { if (this._dragData && this._dragData.dropTarget && this._dragData.dropTarget.key === target.key) { EventGroup.raise(target.root, 'dragleave'); this._dragData.dropTarget = undefined; } } }; /** * when mouse down on a draggable item, we start to track dragdata. */ DragDropHelper.prototype._onMouseDown = function (target, event) { if (event.button !== MOUSEDOWN_PRIMARY_BUTTON) { // Ignore anything except the primary button. return; } if (this._isDraggable(target)) { this._dragData = { clientX: event.clientX, clientY: event.clientY, eventTarget: event.target, dragTarget: target }; for (var _i = 0, _a = Object.keys(this._activeTargets); _i < _a.length; _i++) { var key = _a[_i]; var activeTarget = this._activeTargets[key]; if (activeTarget.target.root) { this._events.on(activeTarget.target.root, 'mousemove', this._onMouseMove.bind(this, activeTarget.target)); this._events.on(activeTarget.target.root, 'mouseleave', this._onMouseLeave.bind(this, activeTarget.target)); } } } else { this._dragData = null; } }; /** * determine whether the child target is a descendant of the parent */ DragDropHelper.prototype._isChild = function (parent, child) { var parentElement = ReactDOM.findDOMNode(parent); var childElement = ReactDOM.findDOMNode(child); while (childElement && childElement.parentElement) { if (childElement.parentElement === parentElement) { return true; } childElement = childElement.parentElement; } return false; }; DragDropHelper.prototype._isDraggable = function (target) { var options = target.options; return !!(options.canDrag && options.canDrag(options.context.data)); }; DragDropHelper.prototype._isDroppable = function (target) { // TODO: take the drag item into consideration to prevent dragging an item into the same group var options = target.options; var dragContext = this._dragData && this._dragData.dragTarget ? this._dragData.dragTarget.options.context : undefined; return !!(options.canDrop && options.canDrop(options.context, dragContext)); }; return DragDropHelper; }()); export { DragDropHelper }; //# sourceMappingURL=DragDropHelper.js.map