Skip to content

Commit dd9bcf1

Browse files
author
Clauderic Demers
committed
Added distance (clauderic#35) and shouldCancelStart (clauderic#47, clauderic#36, clauderic#41) props. Prevent right click from causing sort start (clauderic#46)
1 parent 8577046 commit dd9bcf1

File tree

2 files changed

+76
-24
lines changed

2 files changed

+76
-24
lines changed

README.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,25 @@ More code examples are available [here](https://github.com/clauderic/react-sorta
8989
### Prop Types
9090

9191
#### SortableContainer HOC
92-
| Property | Type | Default | Description |
93-
|:---------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
94-
| axis | String | `y` | The axis you want to sort on, either 'x' or 'y' |
95-
| lockAxis | String | | If you'd like, you can lock movement to an axis while sorting. This is not something that is possible with HTML5 Drag & Drop |
96-
| helperClass | String | | You can provide a class you'd like to add to the sortable helper to add some styles to it |
97-
| transitionDuration | Number | `300` | The duration of the transition when elements shift positions. Set this to `0` if you'd like to disable transitions |
98-
| pressDelay | Number | `0` | If you'd like elements to only become sortable after being pressed for a certain time, change this property. A good sensible default value for mobile is `200` |
99-
| onSortStart | Function | | Callback that get's invoked when sorting begins. `function({node, index, collection}, event)` |
100-
| onSortMove | Function | | Callback that get's invoked during sorting as the cursor moves. `function(event)` |
101-
| onSortEnd | Function | | Callback that get's invoked when sortin ends. `function({oldIndex, newIndex, collection}, e)` |
102-
| useDragHandle | Boolean | `false` | If you're using the `SortableHandle` HOC, set this to `true` |
103-
| useWindowAsScrollContainer | Boolean | `false` | If you want, you can set the `window` as the scrolling container |
104-
| hideSortableGhost | Boolean | `true` | Whether to auto-hide the ghost element. By default, as a convenience, React Sortable List will automatically hide the element that is currently being sorted. Set this to false if you would like to apply your own styling. |
105-
| lockToContainerEdges | Boolean | `false` | You can lock movement of the sortable element to it's parent `SortableContainer` |
106-
| lockOffset | `OffsetValue`\* \| [`OffsetValue`\*, `OffsetValue`\*] | `"50%"` | When `lockToContainerEdges` is set to `true`, this controls the offset distance between the sortable helper and the top/bottom edges of it's parent `SortableContainer`. Percentage values are relative to the height of the item currently being sorted. If you wish to specify different behaviours for locking to the *top* of the container vs the *bottom*, you may also pass in an `array` (For example: `["0%", "100%"]`). |
107-
| getContainer | Function | | Optional function to return the scrollable container element. This property defaults to the `SortableContainer` element itself or (if `useWindowAsScrollContainer` is true) the window. Use this function to specify a custom container object (eg this is useful for integrating with certain 3rd party components such as `FlexTable`). This function is passed a single parameter (the `wrappedInstance` React element) and it is expected to return a DOM element. |
108-
109-
\* `OffsetValue` is either a finite `Number` or a `String` made-up of a number and a unit (`px` or `%`).
110-
Examples: `10` (is the same as `"10px"`), `"50%"`
92+
| Property | Type | Default | Description |
93+
|:---------------------------|:------------------|:-----------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
94+
| axis | String | `y` | The axis you want to sort on, either 'x' or 'y' |
95+
| lockAxis | String | | If you'd like, you can lock movement to an axis while sorting. This is not something that is possible with HTML5 Drag & Drop |
96+
| helperClass | String | | You can provide a class you'd like to add to the sortable helper to add some styles to it |
97+
| transitionDuration | Number | `300` | The duration of the transition when elements shift positions. Set this to `0` if you'd like to disable transitions |
98+
| pressDelay | Number | `0` | If you'd like elements to only become sortable after being pressed for a certain time, change this property. A good sensible default value for mobile is `200`. Cannot be used in conjunction with the `distance` prop. |
99+
| distance | Number | `0` | If you'd like elements to only become sortable after being dragged a certain number of pixels. Cannot be used in conjunction with the `pressDelay` prop. |
100+
| shouldCancelStart | Function | [Function](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/index.js#L34) | This function get's invoked before sorting begins, and can be used to programatically cancel sorting before it begins. By default, it will cancel sorting if the event target is either an `input`, `textarea`, `select` or `option`. |
101+
| onSortStart | Function | | Callback that get's invoked when sorting begins. `function({node, index, collection}, event)` |
102+
| onSortMove | Function | | Callback that get's invoked during sorting as the cursor moves. `function(event)` |
103+
| onSortEnd | Function | | Callback that get's invoked when sorting ends. `function({oldIndex, newIndex, collection}, e)` |
104+
| useDragHandle | Boolean | `false` | If you're using the `SortableHandle` HOC, set this to `true` |
105+
| useWindowAsScrollContainer | Boolean | `false` | If you want, you can set the `window` as the scrolling container |
106+
| hideSortableGhost | Boolean | `true` | Whether to auto-hide the ghost element. By default, as a convenience, React Sortable List will automatically hide the element that is currently being sorted. Set this to false if you would like to apply your own styling. |
107+
| lockToContainerEdges | Boolean | `false` | You can lock movement of the sortable element to it's parent `SortableContainer` |
108+
| lockOffset | `OffsetValue`\* \ | [`OffsetValue`\*, `OffsetValue`\*] | `"50%"` | When `lockToContainerEdges` is set to `true`, this controls the offset distance between the sortable helper and the top/bottom edges of it's parent `SortableContainer`. Percentage values are relative to the height of the item currently being sorted. If you wish to specify different behaviours for locking to the *top* of the container vs the *bottom*, you may also pass in an `array` (For example: `["0%", "100%"]`). |
109+
| getContainer | Function | | Optional function to return the scrollable container element. This property defaults to the `SortableContainer` element itself or (if `useWindowAsScrollContainer` is true) the window. Use this function to specify a custom container object (eg this is useful for integrating with certain 3rd party components such as `FlexTable`). This function is passed a single parameter (the `wrappedInstance` React element) and it is expected to return a DOM element. |
110+
111111
\* `OffsetValue` can either be a finite `Number` or a `String` made up of a number and a unit (`px` or `%`).
112112
Examples: `10` (which is the same as `"10px"`), `"50%"`
113113

src/SortableContainer/index.js

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,49 @@ import invariant from 'invariant';
77
// Export Higher Order Sortable Container Component
88
export default function SortableContainer(WrappedComponent, config = {withRef: false}) {
99
return class extends Component {
10-
constructor() {
10+
constructor(props) {
1111
super();
1212
this.manager = new Manager();
1313
this.events = {
1414
start: this.handleStart,
15-
move: this.cancel,
16-
end: this.cancel
15+
move: this.handleMove,
16+
end: this.handleEnd
1717
};
18+
19+
invariant(
20+
!(props.distance && props.pressDelay),
21+
'Attempted to set both `pressDelay` and `distance` on SortableContainer, you may only use one or the other, not both at the same time.'
22+
)
1823
}
1924
static displayName = (WrappedComponent.displayName) ? `SortableList(${WrappedComponent.displayName})` : 'SortableList';
2025
static WrappedComponent = WrappedComponent;
2126
static defaultProps = {
2227
axis: 'y',
2328
transitionDuration: 300,
2429
pressDelay: 0,
30+
distance: 0,
2531
useWindowAsScrollContainer: false,
2632
hideSortableGhost: true,
27-
contentWindow: (typeof window !== 'undefined') ? window : null,
33+
contentWindow: typeof window !== 'undefined' ? window : null,
34+
shouldCancelStart: function (e) {
35+
if (['input', 'textarea', 'select', 'option'].indexOf(e.target.tagName.toLowerCase()) !== -1) {
36+
return true;
37+
}
38+
},
2839
lockToContainerEdges: false,
2940
lockOffset: '50%'
3041
};
3142
static propTypes = {
3243
axis: PropTypes.oneOf(['x', 'y']),
44+
distance: PropTypes.number,
3345
lockAxis: PropTypes.string,
3446
helperClass: PropTypes.string,
3547
transitionDuration: PropTypes.number,
3648
contentWindow: PropTypes.any,
3749
onSortStart: PropTypes.func,
3850
onSortMove: PropTypes.func,
3951
onSortEnd: PropTypes.func,
52+
shouldCancelStart: PropTypes.func,
4053
pressDelay: PropTypes.number,
4154
useDragHandle: PropTypes.bool,
4255
useWindowAsScrollContainer: PropTypes.bool,
@@ -76,6 +89,16 @@ export default function SortableContainer(WrappedComponent, config = {withRef: f
7689
}
7790
}
7891
handleStart = (e) => {
92+
const {distance, shouldCancelStart} = this.props;
93+
94+
if (e.button === 2 || shouldCancelStart(e)) { return false; }
95+
96+
this._touched = true;
97+
this._pos = {
98+
x: e.clientX,
99+
y: e.clientY
100+
};
101+
79102
let node = closest(e.target, (el) => el.sortableInfo != null);
80103

81104
if (node && !this.state.sorting && node.sortableInfo) {
@@ -85,9 +108,36 @@ export default function SortableContainer(WrappedComponent, config = {withRef: f
85108
if (useDragHandle && !closest(e.target, (el) => el.sortableHandle != null)) return;
86109

87110
this.manager.active = {index, collection};
88-
this.pressTimer = setTimeout(() => this.handlePress(e), this.props.pressDelay);
111+
112+
if (!distance) {
113+
this.pressTimer = setTimeout(() => this.handlePress(e), this.props.pressDelay);
114+
}
89115
}
90116
};
117+
handleMove = (e) => {
118+
const {distance} = this.props;
119+
120+
if (!this.state.sorting && this._touched) {
121+
this._delta = {
122+
x: this._pos.x - e.clientX,
123+
y: this._pos.y - e.clientY
124+
};
125+
let delta = Math.abs(this._delta.x) + Math.abs(this._delta.y);
126+
127+
if (!distance) {
128+
this.cancel();
129+
} else if (delta >= distance) {
130+
this.handlePress(e);
131+
}
132+
}
133+
}
134+
handleEnd = () => {
135+
const {distance} = this.props;
136+
137+
this._touched = false;
138+
139+
if (!distance) { this.cancel(); }
140+
}
91141
cancel = () => {
92142
if (!this.state.sorting) {
93143
clearTimeout(this.pressTimer);
@@ -216,6 +266,8 @@ export default function SortableContainer(WrappedComponent, config = {withRef: f
216266
sorting: false,
217267
sortingIndex: null
218268
});
269+
270+
this._touched = false;
219271
}
220272
getEdgeOffset(edge, node, offset = 0) {
221273
// Get the actual offsetTop / offsetLeft value, no matter how deep the node is nested

0 commit comments

Comments
 (0)