"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var React = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _reactDom = _interopRequireDefault(require("react-dom")); var _domFns = require("./utils/domFns"); var _positionFns = require("./utils/positionFns"); var _shims = require("./utils/shims"); var _log = _interopRequireDefault(require("./utils/log")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /*:: import type {EventHandler, MouseTouchEvent} from './utils/types';*/ /*:: import type {Element as ReactElement} from 'react';*/ // Simple abstraction for dragging events names. const eventsFor = { touch: { start: 'touchstart', move: 'touchmove', stop: 'touchend' }, mouse: { start: 'mousedown', move: 'mousemove', stop: 'mouseup' } }; // Default to mouse events. let dragEventFor = eventsFor.mouse; /*:: export type DraggableData = { node: HTMLElement, x: number, y: number, deltaX: number, deltaY: number, lastX: number, lastY: number, };*/ /*:: export type DraggableEventHandler = (e: MouseEvent, data: DraggableData) => void | false;*/ /*:: export type ControlPosition = {x: number, y: number};*/ /*:: export type PositionOffsetControlPosition = {x: number|string, y: number|string};*/ /*:: export type DraggableCoreDefaultProps = { allowAnyClick: boolean, disabled: boolean, enableUserSelectHack: boolean, onStart: DraggableEventHandler, onDrag: DraggableEventHandler, onStop: DraggableEventHandler, onMouseDown: (e: MouseEvent) => void, scale: number, };*/ /*:: export type DraggableCoreProps = { ...DraggableCoreDefaultProps, cancel: string, children: ReactElement, offsetParent: HTMLElement, grid: [number, number], handle: string, nodeRef?: ?React.ElementRef, };*/ // // Define . // // is for advanced usage of . It maintains minimal internal state so it can // work well with libraries that require more control over the element. // class DraggableCore extends React.Component /*:: */{ constructor() { super(...arguments); _defineProperty(this, "dragging", false); // Used while dragging to determine deltas. _defineProperty(this, "lastX", NaN); _defineProperty(this, "lastY", NaN); _defineProperty(this, "touchIdentifier", null); _defineProperty(this, "mounted", false); _defineProperty(this, "handleDragStart", e => { // Make it possible to attach event handlers on top of this one. this.props.onMouseDown(e); // Only accept left-clicks. if (!this.props.allowAnyClick && typeof e.button === 'number' && e.button !== 0) return false; // Get nodes. Be sure to grab relative document (could be iframed) const thisNode = this.findDOMNode(); if (!thisNode || !thisNode.ownerDocument || !thisNode.ownerDocument.body) { throw new Error(' not mounted on DragStart!'); } const { ownerDocument } = thisNode; // Short circuit if handle or cancel prop was provided and selector doesn't match. if (this.props.disabled || !(e.target instanceof ownerDocument.defaultView.Node) || this.props.handle && !(0, _domFns.matchesSelectorAndParentsTo)(e.target, this.props.handle, thisNode) || this.props.cancel && (0, _domFns.matchesSelectorAndParentsTo)(e.target, this.props.cancel, thisNode)) { return; } // Prevent scrolling on mobile devices, like ipad/iphone. // Important that this is after handle/cancel. if (e.type === 'touchstart') e.preventDefault(); // Set touch identifier in component state if this is a touch event. This allows us to // distinguish between individual touches on multitouch screens by identifying which // touchpoint was set to this element. const touchIdentifier = (0, _domFns.getTouchIdentifier)(e); this.touchIdentifier = touchIdentifier; // Get the current drag point from the event. This is used as the offset. const position = (0, _positionFns.getControlPosition)(e, touchIdentifier, this); if (position == null) return; // not possible but satisfies flow const { x, y } = position; // Create an event object with all the data parents need to make a decision here. const coreEvent = (0, _positionFns.createCoreData)(this, x, y); (0, _log.default)('DraggableCore: handleDragStart: %j', coreEvent); // Call event handler. If it returns explicit false, cancel. (0, _log.default)('calling', this.props.onStart); const shouldUpdate = this.props.onStart(e, coreEvent); if (shouldUpdate === false || this.mounted === false) return; // Add a style to the body to disable user-select. This prevents text from // being selected all over the page. if (this.props.enableUserSelectHack) (0, _domFns.addUserSelectStyles)(ownerDocument); // Initiate dragging. Set the current x and y as offsets // so we know how much we've moved during the drag. This allows us // to drag elements around even if they have been moved, without issue. this.dragging = true; this.lastX = x; this.lastY = y; // Add events to the document directly so we catch when the user's mouse/touch moves outside of // this element. We use different events depending on whether or not we have detected that this // is a touch-capable device. (0, _domFns.addEvent)(ownerDocument, dragEventFor.move, this.handleDrag); (0, _domFns.addEvent)(ownerDocument, dragEventFor.stop, this.handleDragStop); }); _defineProperty(this, "handleDrag", e => { // Get the current drag point from the event. This is used as the offset. const position = (0, _positionFns.getControlPosition)(e, this.touchIdentifier, this); if (position == null) return; let { x, y } = position; // Snap to grid if prop has been provided if (Array.isArray(this.props.grid)) { let deltaX = x - this.lastX, deltaY = y - this.lastY; [deltaX, deltaY] = (0, _positionFns.snapToGrid)(this.props.grid, deltaX, deltaY); if (!deltaX && !deltaY) return; // skip useless drag x = this.lastX + deltaX, y = this.lastY + deltaY; } const coreEvent = (0, _positionFns.createCoreData)(this, x, y); (0, _log.default)('DraggableCore: handleDrag: %j', coreEvent); // Call event handler. If it returns explicit false, trigger end. const shouldUpdate = this.props.onDrag(e, coreEvent); if (shouldUpdate === false || this.mounted === false) { try { // $FlowIgnore this.handleDragStop(new MouseEvent('mouseup')); } catch (err) { // Old browsers const event = ((document.createEvent('MouseEvents') /*: any*/) /*: MouseTouchEvent*/); // I see why this insanity was deprecated // $FlowIgnore event.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); this.handleDragStop(event); } return; } this.lastX = x; this.lastY = y; }); _defineProperty(this, "handleDragStop", e => { if (!this.dragging) return; const position = (0, _positionFns.getControlPosition)(e, this.touchIdentifier, this); if (position == null) return; let { x, y } = position; // Snap to grid if prop has been provided if (Array.isArray(this.props.grid)) { let deltaX = x - this.lastX || 0; let deltaY = y - this.lastY || 0; [deltaX, deltaY] = (0, _positionFns.snapToGrid)(this.props.grid, deltaX, deltaY); x = this.lastX + deltaX, y = this.lastY + deltaY; } const coreEvent = (0, _positionFns.createCoreData)(this, x, y); // Call event handler const shouldContinue = this.props.onStop(e, coreEvent); if (shouldContinue === false || this.mounted === false) return false; const thisNode = this.findDOMNode(); if (thisNode) { // Remove user-select hack if (this.props.enableUserSelectHack) (0, _domFns.removeUserSelectStyles)(thisNode.ownerDocument); } (0, _log.default)('DraggableCore: handleDragStop: %j', coreEvent); // Reset the el. this.dragging = false; this.lastX = NaN; this.lastY = NaN; if (thisNode) { // Remove event handlers (0, _log.default)('DraggableCore: Removing handlers'); (0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.move, this.handleDrag); (0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.stop, this.handleDragStop); } }); _defineProperty(this, "onMouseDown", e => { dragEventFor = eventsFor.mouse; // on touchscreen laptops we could switch back to mouse return this.handleDragStart(e); }); _defineProperty(this, "onMouseUp", e => { dragEventFor = eventsFor.mouse; return this.handleDragStop(e); }); // Same as onMouseDown (start drag), but now consider this a touch device. _defineProperty(this, "onTouchStart", e => { // We're on a touch device now, so change the event handlers dragEventFor = eventsFor.touch; return this.handleDragStart(e); }); _defineProperty(this, "onTouchEnd", e => { // We're on a touch device now, so change the event handlers dragEventFor = eventsFor.touch; return this.handleDragStop(e); }); } componentDidMount() { this.mounted = true; // Touch handlers must be added with {passive: false} to be cancelable. // https://developers.google.com/web/updates/2017/01/scrolling-intervention const thisNode = this.findDOMNode(); if (thisNode) { (0, _domFns.addEvent)(thisNode, eventsFor.touch.start, this.onTouchStart, { passive: false }); } } componentWillUnmount() { this.mounted = false; // Remove any leftover event handlers. Remove both touch and mouse handlers in case // some browser quirk caused a touch event to fire during a mouse move, or vice versa. const thisNode = this.findDOMNode(); if (thisNode) { const { ownerDocument } = thisNode; (0, _domFns.removeEvent)(ownerDocument, eventsFor.mouse.move, this.handleDrag); (0, _domFns.removeEvent)(ownerDocument, eventsFor.touch.move, this.handleDrag); (0, _domFns.removeEvent)(ownerDocument, eventsFor.mouse.stop, this.handleDragStop); (0, _domFns.removeEvent)(ownerDocument, eventsFor.touch.stop, this.handleDragStop); (0, _domFns.removeEvent)(thisNode, eventsFor.touch.start, this.onTouchStart, { passive: false }); if (this.props.enableUserSelectHack) (0, _domFns.removeUserSelectStyles)(ownerDocument); } } // React Strict Mode compatibility: if `nodeRef` is passed, we will use it instead of trying to find // the underlying DOM node ourselves. See the README for more information. findDOMNode() /*: ?HTMLElement*/{ var _this$props, _this$props2; return (_this$props = this.props) !== null && _this$props !== void 0 && _this$props.nodeRef ? (_this$props2 = this.props) === null || _this$props2 === void 0 || (_this$props2 = _this$props2.nodeRef) === null || _this$props2 === void 0 ? void 0 : _this$props2.current : _reactDom.default.findDOMNode(this); } render() /*: React.Element*/{ // Reuse the child provided // This makes it flexible to use whatever element is wanted (div, ul, etc) return /*#__PURE__*/React.cloneElement(React.Children.only(this.props.children), { // Note: mouseMove handler is attached to document so it will still function // when the user drags quickly and leaves the bounds of the element. onMouseDown: this.onMouseDown, onMouseUp: this.onMouseUp, // onTouchStart is added on `componentDidMount` so they can be added with // {passive: false}, which allows it to cancel. See // https://developers.google.com/web/updates/2017/01/scrolling-intervention onTouchEnd: this.onTouchEnd }); } } exports.default = DraggableCore; _defineProperty(DraggableCore, "displayName", 'DraggableCore'); _defineProperty(DraggableCore, "propTypes", { /** * `allowAnyClick` allows dragging using any mouse button. * By default, we only accept the left button. * * Defaults to `false`. */ allowAnyClick: _propTypes.default.bool, children: _propTypes.default.node.isRequired, /** * `disabled`, if true, stops the from dragging. All handlers, * with the exception of `onMouseDown`, will not fire. */ disabled: _propTypes.default.bool, /** * By default, we add 'user-select:none' attributes to the document body * to prevent ugly text selection during drag. If this is causing problems * for your app, set this to `false`. */ enableUserSelectHack: _propTypes.default.bool, /** * `offsetParent`, if set, uses the passed DOM node to compute drag offsets * instead of using the parent node. */ offsetParent: function (props /*: DraggableCoreProps*/, propName /*: $Keys*/) { if (props[propName] && props[propName].nodeType !== 1) { throw new Error('Draggable\'s offsetParent must be a DOM Node.'); } }, /** * `grid` specifies the x and y that dragging should snap to. */ grid: _propTypes.default.arrayOf(_propTypes.default.number), /** * `handle` specifies a selector to be used as the handle that initiates drag. * * Example: * * ```jsx * let App = React.createClass({ * render: function () { * return ( * *
*
Click me to drag
*
This is some other content
*
*
* ); * } * }); * ``` */ handle: _propTypes.default.string, /** * `cancel` specifies a selector to be used to prevent drag initialization. * * Example: * * ```jsx * let App = React.createClass({ * render: function () { * return( * *
*
You can't drag from here
*
Dragging here works fine
*
*
* ); * } * }); * ``` */ cancel: _propTypes.default.string, /* If running in React Strict mode, ReactDOM.findDOMNode() is deprecated. * Unfortunately, in order for to work properly, we need raw access * to the underlying DOM node. If you want to avoid the warning, pass a `nodeRef` * as in this example: * * function MyComponent() { * const nodeRef = React.useRef(null); * return ( * *
Example Target
*
* ); * } * * This can be used for arbitrarily nested components, so long as the ref ends up * pointing to the actual child DOM node and not a custom component. */ nodeRef: _propTypes.default.object, /** * Called when dragging starts. * If this function returns the boolean false, dragging will be canceled. */ onStart: _propTypes.default.func, /** * Called while dragging. * If this function returns the boolean false, dragging will be canceled. */ onDrag: _propTypes.default.func, /** * Called when dragging stops. * If this function returns the boolean false, the drag will remain active. */ onStop: _propTypes.default.func, /** * A workaround option which can be passed if onMouseDown needs to be accessed, * since it'll always be blocked (as there is internal use of onMouseDown) */ onMouseDown: _propTypes.default.func, /** * `scale`, if set, applies scaling while dragging an element */ scale: _propTypes.default.number, /** * These properties should be defined on the child, not here. */ className: _shims.dontSetMe, style: _shims.dontSetMe, transform: _shims.dontSetMe }); _defineProperty(DraggableCore, "defaultProps", { allowAnyClick: false, // by default only accept left click disabled: false, enableUserSelectHack: true, onStart: function () {}, onDrag: function () {}, onStop: function () {}, onMouseDown: function () {}, scale: 1 });