UNPKG

apeman-react-touchable

Version:
158 lines (146 loc) 4.25 kB
/** * Higher order component with touching feature * @function withTouch * @param {function} Component - A component constructor * @returns {function} - Wrapped component */ 'use strict' import {wrap} from 'breact' import React, {PropTypes as types} from 'react' import ReactDOM from 'react-dom' import {clone} from 'asobj' import {directions} from './enums' import newHammer from './helpers/new_hammer' const propTypes = { /** Handler for "tap" event */ onTap: types.func, /** Handler for "doubletap" event */ onDoubleTap: types.func, /** Handler for "pan" event */ onPan: types.func, /** Handler for "panstart" event */ onPanStart: types.func, /** Handler for "panmove" event */ onPanMove: types.func, /** Handler for "panend" event */ onPanEnd: types.func, /** Handler for "pancancel" event */ onPanCancel: types.func, /** Handler for "swipe" event */ onSwipe: types.func, /** Handler for "press" event */ onPress: types.func, /** Handler for "pressup" event */ onPressUp: types.func, /** Handler for "pinch" event */ onPinch: types.func, /** Handler for "pinchstart" event */ onPinchStart: types.func, /** Handler for "pinchmove" event */ onPinchMove: types.func, /** Handler for "pinchend" event */ onPinchEnd: types.func, /** Handler for "pinchcancel" event */ onPinchCancel: types.func, /** Handler for "rotate" event */ onRotate: types.func, /** Handler for "rotatestart" event */ onRotateStart: types.func, /** Handler for "rotatemove" event */ onRotateMove: types.func, /** Handler for "rotateend" event */ onRotateEnd: types.func, /** Handler for "rotatecancel" event */ onRotateCancel: types.func, /** Options for touch */ touchOptions: types.object, /** Pixcel for pan threshold */ panThreshold: types.number, /** Direction for pan */ panDirection: types.oneOf(directions), /** Direction for swipe */ swipeDirection: types.oneOf(directions) } function createTouchHammer (node, props, getData) { if (!node) { return } let hammer = newHammer(node) if (props.touchOptions) { hammer.set(props.touchOptions) } Object.keys(propTypes) .filter((key) => !!props[ key ]) .forEach((key) => { if (/^on/.test(key)) { let event = key.replace(/^on/, '').toLowerCase() let handler = props[ key ] hammer.on(event, (e) => { e.data = getData() // Set touching data. handler(e) }) } if (/Direction$/.test(key)) { let gesture = key.replace(/Direction$/, '') hammer.get(gesture).set({ direction: hammer.Hammer[ 'DIRECTION_' + props[ key ] ] }) } if (/Threshold$/.test(key)) { let threshold = key.replace(/Threshold$/, '') hammer.get(threshold).set({ threshold: props[ key ] }) } }) return hammer } function destroyTouchHammer (hammer) { if (!hammer) { return } hammer.stop() hammer.destroy() } function supportsTouch (props) { let touchableKey = Object.keys(propTypes) for (let key of Object.keys(props)) { let hit = !!~touchableKey.indexOf(key) if (hit) { return true } } return false } /** @lends withTouch */ function withTouch (Component, config = {}) { let { getTouchData } = config let spec = Object.assign({ displayName: 'withTouch', propTypes, render () { const s = this let { props } = s let wrappedProps = clone(props, { without: Object.keys(propTypes || {}) }) wrappedProps.hasTouch = !!(props.onTap) let children = React.Children.toArray(props.children) return React.createElement(Component, wrappedProps, ...children) }, componentDidMount () { const s = this let touchable = supportsTouch(s.props) let getData = () => s.getTouchData ? s.getTouchData() : undefined if (touchable) { s.touchHammer = createTouchHammer(ReactDOM.findDOMNode(s), s.props, getData) } }, componentWillUnmount () { const s = this let hammer = s.touchHammer if (hammer) { destroyTouchHammer(hammer) } delete s.touchHammer } }, { getTouchData }) return wrap(Component, spec) } export default withTouch