diff --git a/README.md b/README.md index 50cfefd6..cdffebc0 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,14 @@ type DraggableData = { // If set to `true`, will allow dragging on non left-button clicks. allowAnyClick: boolean, +// Default `false` and default behavior before 4.5.0. +// If set to `true`, the 'touchstart' event will not be prevented, +// which will allow scrolling inside containers. We recommend +// using the 'handle' / 'cancel' props when possible instead of enabling this. +// +// See https://github.com/react-grid-layout/react-draggable/issues/728 +allowMobileScroll: boolean, + // Determines which axis the draggable can move. This only affects // flushing to the DOM. Callbacks will still include all values. // Accepted values: @@ -202,6 +210,9 @@ defaultPosition: {x: number, y: number}, // If true, will not call any drag handlers. disabled: boolean, +// Default `true`. Adds "user-select: none" while dragging to avoid selecting text. +enableUserSelectHack: boolean, + // Specifies the x and y that dragging should snap to. grid: [number, number], @@ -324,6 +335,7 @@ on itself and thus must have callbacks attached to be useful. ```js { allowAnyClick: boolean, + allowMobileScroll: boolean, cancel: string, disabled: boolean, enableUserSelectHack: boolean, diff --git a/lib/DraggableCore.js b/lib/DraggableCore.js index 6b464aa0..cae61661 100644 --- a/lib/DraggableCore.js +++ b/lib/DraggableCore.js @@ -42,6 +42,7 @@ export type PositionOffsetControlPosition = {x: number|string, y: number|string} export type DraggableCoreDefaultProps = { allowAnyClick: boolean, + allowMobileScroll: boolean, disabled: boolean, enableUserSelectHack: boolean, onStart: DraggableEventHandler, @@ -81,6 +82,15 @@ export default class DraggableCore extends React.Component { */ allowAnyClick: PropTypes.bool, + /** + * `allowMobileScroll` turns off cancellation of the 'touchstart' event + * on mobile devices. Only enable this if you are having trouble with click + * events. Prefer using 'handle' / 'cancel' instead. + * + * Defaults to `false`. + */ + allowMobileScroll: PropTypes.bool, + children: PropTypes.node.isRequired, /** @@ -213,6 +223,7 @@ export default class DraggableCore extends React.Component { static defaultProps: DraggableCoreDefaultProps = { allowAnyClick: false, // by default only accept left click + allowMobileScroll: false, disabled: false, enableUserSelectHack: true, onStart: function(){}, @@ -288,7 +299,7 @@ export default class DraggableCore extends React.Component { // Prevent scrolling on mobile devices, like ipad/iphone. // Important that this is after handle/cancel. - if (e.type === 'touchstart') e.preventDefault(); + if (e.type === 'touchstart' && !this.props.allowMobileScroll) 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 diff --git a/specs/draggable.spec.jsx b/specs/draggable.spec.jsx index 51ed3b4d..143eae96 100644 --- a/specs/draggable.spec.jsx +++ b/specs/draggable.spec.jsx @@ -622,6 +622,18 @@ describe('react-draggable', function () { assert.equal(drag.state.dragging, true); }); + it('should *not* call preventDefault on touchStart event if "allowMobileScroll"', function () { + drag = TestUtils.renderIntoDocument(
); + + const e = new Event('touchstart'); + // Oddly `e.defaultPrevented` is not changing here. Maybe because we're not mounted to a real doc? + let pdCalled = false; + e.preventDefault = function() { pdCalled = true; }; + ReactDOM.findDOMNode(drag).dispatchEvent(e); + assert(!pdCalled); + assert.equal(drag.state.dragging, true); + }); + it('should not call preventDefault on touchStart event if not on handle', function () { drag = TestUtils.renderIntoDocument( diff --git a/typings/index.d.ts b/typings/index.d.ts index bcbc6ff3..2ad9ec79 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -42,6 +42,7 @@ declare module 'react-draggable' { export interface DraggableCoreProps { allowAnyClick: boolean, + allowMobileScroll: boolean, cancel: string, children?: React.ReactNode, disabled: boolean, diff --git a/typings/test.tsx b/typings/test.tsx index 87562d49..0532f949 100644 --- a/typings/test.tsx +++ b/typings/test.tsx @@ -20,8 +20,9 @@ ReactDOM.render( onDrag={handleDrag} onStop={handleStop} offsetParent={document.body} - allowAnyClick={true} onMouseDown={handleMouseDown} + allowAnyClick={true} + allowMobileScroll={false} disabled={true} enableUserSelectHack={false} bounds={false} @@ -54,7 +55,8 @@ ReactDOM.render( onDrag={handleDrag} onStop={handleStop} offsetParent={document.body} - enableUserSelectHack={false}> + enableUserSelectHack={false} + allowMobileScroll={false}>