apeman-react-touchable
Version:
apeman react package for touchable component.
158 lines (146 loc) • 4.25 kB
JSX
/**
* Higher order component with touching feature
* @function withTouch
* @param {function} Component - A component constructor
* @returns {function} - Wrapped component
*/
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