// @flow import { Component } from 'react'; import memoizeOne from 'memoize-one'; import type { Props, Provided, } from './drag-handle-types'; import type { Sensor, MouseSensor, KeyboardSensor, TouchSensor, } from './sensor/sensor-types'; import createMouseSensor from './sensor/create-mouse-sensor'; import createKeyboardSensor from './sensor/create-keyboard-sensor'; import createTouchSensor from './sensor/create-touch-sensor'; const getFalse: () => boolean = () => false; export default class DragHandle extends Component { /* eslint-disable react/sort-comp */ props: Props mouseSensor: MouseSensor = createMouseSensor( this.props.callbacks, this.props.getDraggableRef ); keyboardSensor: KeyboardSensor = createKeyboardSensor( this.props.callbacks, this.props.getDraggableRef ); touchSensor: TouchSensor = createTouchSensor( this.props.callbacks, this.props.getDraggableRef ); sensors: Sensor[] = [ this.mouseSensor, this.keyboardSensor, this.touchSensor, ]; componentWillUnmount() { this.sensors.forEach((sensor: Sensor) => { // kill the current drag and fire a cancel event if const wasCapturing = sensor.isCapturing(); const wasDragging = sensor.isDragging(); // stop capturing if (wasCapturing) { sensor.kill(); } // cancel if drag was occurring if (wasDragging) { this.props.callbacks.onCancel(); } }); } componentWillReceiveProps(nextProps: Props) { const isCapturing: boolean = this.isAnySensorCapturing(); if (!isCapturing) { return; } const isDragStopping: boolean = (this.props.isDragging && !nextProps.isDragging); // if the application cancels a drag we need to unbind the handlers if (isDragStopping) { this.sensors.forEach((sensor: Sensor) => { if (sensor.isCapturing()) { sensor.kill(); // not firing any cancel event as the drag is already over } }); return; } // dragging disabled mid drag if (!nextProps.isEnabled) { this.sensors.forEach((sensor: Sensor) => { if (sensor.isCapturing()) { const wasDragging: boolean = sensor.isDragging(); // stop listening sensor.kill(); // we need to cancel the drag if it was dragging if (wasDragging) { this.props.callbacks.onCancel(); } } }); } } onKeyDown = (event: MouseEvent) => { // let the mouse sensor deal with it if (this.mouseSensor.isCapturing()) { return; } this.keyboardSensor.onKeyDown(event, this.props); } onMouseDown = (event: MouseEvent) => { // let the keyboard sensor deal with it if it is capturing if (this.keyboardSensor.isCapturing()) { return; } this.mouseSensor.onMouseDown(event, this.props); } onTouchStart = (event: TouchEvent) => { // let the keyboard sensor deal with it if (this.mouseSensor.isCapturing() || this.keyboardSensor.isCapturing()) { console.error('mouse or keyboard already listening when attempting to touch drag'); return; } this.touchSensor.onTouchStart(event, this.props); } onTouchMove = (event: TouchEvent) => { this.touchSensor.onTouchMove(event); } onClick = (event: MouseEvent) => { // The mouse or touch sensor may want to block the click this.mouseSensor.onClick(event); this.touchSensor.onClick(event); } isAnySensorDragging = () => this.sensors.some((sensor: Sensor) => sensor.isDragging()) isAnySensorCapturing = () => this.sensors.some((sensor: Sensor) => sensor.isCapturing()) getProvided = memoizeOne((isEnabled: boolean, isDragging: boolean): ?Provided => { if (!isEnabled) { return null; } const provided: Provided = { onMouseDown: this.onMouseDown, onKeyDown: this.onKeyDown, onTouchStart: this.onTouchStart, onTouchMove: this.onTouchMove, onClick: this.onClick, tabIndex: 0, 'aria-grabbed': isDragging, draggable: false, onDragStart: getFalse, onDrop: getFalse, }; return provided; }) render() { const { children, isEnabled } = this.props; return children(this.getProvided(isEnabled, this.isAnySensorDragging())); } }