office-ui-fabric-react
Version:
Reusable React components for building experiences for Microsoft 365.
268 lines • 10.9 kB
JavaScript
import { __assign, __extends } from "tslib";
import * as React from 'react';
import { getClassNames } from './DraggableZone.styles';
import { on } from '../../Utilities';
var eventMapping = {
touch: {
start: 'touchstart',
move: 'touchmove',
stop: 'touchend',
},
mouse: {
start: 'mousedown',
move: 'mousemove',
stop: 'mouseup',
},
};
var DraggableZone = /** @class */ (function (_super) {
__extends(DraggableZone, _super);
function DraggableZone(props) {
var _this = _super.call(this, props) || this;
_this._currentEventType = eventMapping.mouse;
_this._events = [];
_this._onMouseDown = function (event) {
var onMouseDown = React.Children.only(_this.props.children).props.onMouseDown;
if (onMouseDown) {
onMouseDown(event);
}
_this._currentEventType = eventMapping.mouse;
return _this._onDragStart(event);
};
_this._onMouseUp = function (event) {
var onMouseUp = React.Children.only(_this.props.children).props.onMouseUp;
if (onMouseUp) {
onMouseUp(event);
}
_this._currentEventType = eventMapping.mouse;
return _this._onDragStop(event);
};
_this._onTouchStart = function (event) {
var onTouchStart = React.Children.only(_this.props.children).props.onTouchStart;
if (onTouchStart) {
onTouchStart(event);
}
_this._currentEventType = eventMapping.touch;
return _this._onDragStart(event);
};
_this._onTouchEnd = function (event) {
var onTouchEnd = React.Children.only(_this.props.children).props.onTouchEnd;
if (onTouchEnd) {
onTouchEnd(event);
}
_this._currentEventType = eventMapping.touch;
_this._onDragStop(event);
};
_this._onDragStart = function (event) {
// Only handle left click for dragging
if (typeof event.button === 'number' && event.button !== 0) {
return false;
}
// If the target doesn't match the handleSelector OR
// if the target does match the preventDragSelector, bail out
if ((_this.props.handleSelector && !_this._matchesSelector(event.target, _this.props.handleSelector)) ||
(_this.props.preventDragSelector &&
_this._matchesSelector(event.target, _this.props.preventDragSelector))) {
return;
}
// Remember the touch identifier if this is a touch event so we can
// distinguish between individual touches in multitouch scenarios
// by remembering which touch point we were given
_this._touchId = _this._getTouchId(event);
var position = _this._getControlPosition(event);
if (position === undefined) {
return;
}
var dragData = _this._createDragDataFromPosition(position);
_this.props.onStart && _this.props.onStart(event, dragData);
_this.setState({
isDragging: true,
lastPosition: position,
});
// hook up the appropriate mouse/touch events to the body to ensure
// smooth dragging
_this._events = [
on(document.body, _this._currentEventType.move, _this._onDrag, true /* use capture phase */),
on(document.body, _this._currentEventType.stop, _this._onDragStop, true /* use capture phase */),
];
};
_this._onDrag = function (event) {
// Prevent scrolling on mobile devices
if (event.type === 'touchmove') {
event.preventDefault();
}
var position = _this._getControlPosition(event);
if (!position) {
return;
}
// create the updated drag data from the position data
var updatedData = _this._createUpdatedDragData(_this._createDragDataFromPosition(position));
var updatedPosition = updatedData.position;
_this.props.onDragChange && _this.props.onDragChange(event, updatedData);
_this.setState({
position: updatedPosition,
lastPosition: position,
});
};
_this._onDragStop = function (event) {
if (!_this.state.isDragging) {
return;
}
var position = _this._getControlPosition(event);
if (!position) {
return;
}
var baseDragData = _this._createDragDataFromPosition(position);
// Set dragging to false and reset the lastPosition
_this.setState({
isDragging: false,
lastPosition: undefined,
});
_this.props.onStop && _this.props.onStop(event, baseDragData);
if (_this.props.position) {
_this.setState({
position: _this.props.position,
});
}
// Remove event handlers
_this._events.forEach(function (dispose) { return dispose(); });
};
_this.state = {
isDragging: false,
position: _this.props.position || { x: 0, y: 0 },
lastPosition: undefined,
};
return _this;
}
DraggableZone.prototype.componentDidUpdate = function (prevProps) {
if (this.props.position && (!prevProps.position || this.props.position !== prevProps.position)) {
this.setState({ position: this.props.position });
}
};
DraggableZone.prototype.componentWillUnmount = function () {
this._events.forEach(function (dispose) { return dispose(); });
};
DraggableZone.prototype.render = function () {
var child = React.Children.only(this.props.children);
var props = child.props;
var position = this.props.position;
var _a = this.state, statePosition = _a.position, isDragging = _a.isDragging;
var x = statePosition.x;
var y = statePosition.y;
if (position && !isDragging) {
x = position.x;
y = position.y;
}
return React.cloneElement(child, {
style: __assign(__assign({}, props.style), { transform: "translate(" + x + "px, " + y + "px)" }),
className: getClassNames(props.className, this.state.isDragging).root,
onMouseDown: this._onMouseDown,
onMouseUp: this._onMouseUp,
onTouchStart: this._onTouchStart,
onTouchEnd: this._onTouchEnd,
});
};
/**
* Get the control position based off the event that fired
* @param event - The event to get offsets from
*/
DraggableZone.prototype._getControlPosition = function (event) {
var touchObj = this._getActiveTouch(event);
// did we get the right touch?
if (this._touchId !== undefined && !touchObj) {
return undefined;
}
var eventToGetOffset = touchObj || event;
return {
x: eventToGetOffset.clientX,
y: eventToGetOffset.clientY,
};
};
/**
* Get the active touch point that we have saved from the event's TouchList
* @param event - The event used to get the TouchList for the active touch point
*/
DraggableZone.prototype._getActiveTouch = function (event) {
return ((event.targetTouches && this._findTouchInTouchList(event.targetTouches)) ||
(event.changedTouches && this._findTouchInTouchList(event.changedTouches)));
};
/**
* Get the initial touch identifier associated with the given event
* @param event - The event that contains the TouchList
*/
DraggableZone.prototype._getTouchId = function (event) {
var touch = (event.targetTouches && event.targetTouches[0]) || (event.changedTouches && event.changedTouches[0]);
if (touch) {
return touch.identifier;
}
};
/**
* Returns if an element (or any of the element's parents) match the given selector
*/
DraggableZone.prototype._matchesSelector = function (element, selector) {
if (!element || element === document.body) {
return false;
}
var matchesSelectorFn = element.matches || element.webkitMatchesSelector || element.msMatchesSelector /* for IE */;
if (!matchesSelectorFn) {
return false;
}
return matchesSelectorFn.call(element, selector) || this._matchesSelector(element.parentElement, selector);
};
/**
* Attempts to find the Touch that matches the identifier we stored in dragStart
* @param touchList The TouchList to look for the stored identifier from dragStart
*/
DraggableZone.prototype._findTouchInTouchList = function (touchList) {
if (this._touchId === undefined) {
return;
}
for (var i = 0; i < touchList.length; i++) {
if (touchList[i].identifier === this._touchId) {
return touchList[i];
}
}
return undefined;
};
/**
* Create DragData based off of the last known position and the new position passed in
* @param position The new position as part of the drag
*/
DraggableZone.prototype._createDragDataFromPosition = function (position) {
var lastPosition = this.state.lastPosition;
// If we have no lastPosition, use the given position
// for last position
if (lastPosition === undefined) {
return {
delta: { x: 0, y: 0 },
lastPosition: position,
position: position,
};
}
return {
delta: {
x: position.x - lastPosition.x,
y: position.y - lastPosition.y,
},
lastPosition: lastPosition,
position: position,
};
};
/**
* Creates an updated DragData based off the current position and given baseDragData
* @param baseDragData The base DragData (from _createDragDataFromPosition) used to calculate the updated positions
*/
DraggableZone.prototype._createUpdatedDragData = function (baseDragData) {
var position = this.state.position;
return {
position: {
x: position.x + baseDragData.delta.x,
y: position.y + baseDragData.delta.y,
},
delta: baseDragData.delta,
lastPosition: position,
};
};
return DraggableZone;
}(React.Component));
export { DraggableZone };
//# sourceMappingURL=DraggableZone.js.map