Skip to content

Instantly share code, notes, and snippets.

@clintharris
Created February 3, 2017 18:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clintharris/b552f61120c593cccdd37063372dd77f to your computer and use it in GitHub Desktop.
Save clintharris/b552f61120c593cccdd37063372dd77f to your computer and use it in GitHub Desktop.
class MyDraggableList extends DraggableList {
constructor(props) {
super(props);
// We need to override and intercept calls to the _handleMouseMove() function defined in the DraggableList class.
// However, this function is NOT an instance method; we cannot simply override it. `this._handleMouseMove` is
// actually just an instance variable pointing to a function, so the way we "override" it is by modifying that
// variable to point to our custom implementation (_newHandleMouseMove) while keeping a pointer to the original
// function so we can still call it. Many Bothans died to bring us this information.
this._oldHandleMouseMove = this._handleMouseMove;
this._handleMouseMove = this._newHandleMouseMove;
}
/**
* Override the default _handleStartDrag() method inherited from DraggableList.
*
* @param {string|number} itemKey some sort of value that uniquely identifies the item in the list
* @param {number} pressY y-coord of where touch (i.e., touch-screen device) started
* @param {number} pageY y-coord of where mouse click started
*/
_handleStartDrag(itemKey, pressY, pageY) {
super._handleStartDrag(itemKey, pressY, pageY);
// Override the cursor style set in super._handleStartDrag() and use the "grabbing hand" cursor instead. Note that
// the browser-specific prefix MUST be used for 'grabbing' to work.
if (document.documentElement) {
document.documentElement.style.cursor = `${Browser.cssPrefix}grabbing`;
}
// Important: we assume that the list items are instances of our custom MyDraggableListItem class, which specifically
// keeps references to _rootEl and _dragHandleEl.
const { _rootEl: listItemEl, _dragHandleEl: listItemDragHandleEl } = this.getItemInstance(itemKey);
// Note: the coordinates returned by getBoundingClientRect() are relative to the viewport.
const { top: containerTopY, bottom: containerBottomY } = super._getContainer().getBoundingClientRect();
const { top: listItemTopY, height: listItemHeight } = listItemEl.getBoundingClientRect();
const { top: dragHandleTopY, height: dragHandleHeight } = listItemDragHandleEl.getBoundingClientRect();
const topOfItemToMiddleOfHandle = dragHandleTopY - listItemTopY + (dragHandleHeight / 2);
// Calculate a upper and lower boundaries for the mouse. If the mouse goes above or these lines, then we'll
// start rejecting "mousemove" events to prevent list item from being dragged outside (i.e., above the top, or below
// the bottom of) the container.
this._lowerMouseBoundary =
containerBottomY - listItemHeight // Start by using the top of the list item as the lower mouse boundary...
+ topOfItemToMiddleOfHandle // ...now move boundary line down so it's in line with the v-middle of the drag handle
+ 5; // ...then move the boundary down slightly so item can be dragged beyond the bottom of container a wee bit.
this._upperMouseBoundary =
containerTopY // Start by using the top of the list container as the upper mouse boundary...
+ topOfItemToMiddleOfHandle // ...now move boundary line down so it's in line with the v-middle of the drag handle
- 5; // ...then move the boundary up slightly so item can be dragged beyond the top of container a wee bit.
}
/**
* Our custom "handle mouse move" interceptor that will reject drag events if the mouse goes outside of set bounds.
*
* @param {long} options.pageY y-coord of mouse relative to the document (not just the view port), regardless of
* the current scrolling position. For example, the whole page may be 10 million pixels
* tall, but the viewport is only 800px tall. This coord represents the absolute position
* of the mouse within the 10M pixel tall document.
* @param {long} options.clientY y-coord of mouse pointer relative to viewport (regardless of the scroll position).
*/
_newHandleMouseMove({pageY, clientY}) {
if(clientY < this._upperMouseBoundary || clientY > this._lowerMouseBoundary) {
return;
}
this._oldHandleMouseMove({pageY, clientY});
}
}
class MyDraggableListItem extends React.PureComponent {
render() {
const {item, itemSelected, anySelected, dragHandle } = this.props;
const draggableHandle = dragHandle(
<div ref={(c) => this._dragHandleEl = c }>
// some sort of icon here...
</div>
);
return (
<div ref={(c) => this._rootEl = c}>
{ draggableHandle }
{ item }
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment