apeman-react-mixins
Version:
React mixin set of apeman.
112 lines (89 loc) • 1.97 kB
JSX
/**
* Mixin to detect outside tap.
* @mixin ApOutsideMixin
*/
import React, {PropTypes as types} from 'react'
import ReactDOM from 'react-dom'
import defaults from 'defaults'
class BodyTapRecognizer {
constructor () {
const s = this
s._hammer = null
}
getDOMNode () {
const s = this
return document.body
}
getHammer () {
const s = this
let { _hammer } = s
if (_hammer) {
return _hammer
}
const Hammer = require('hammerjs')
s._hammer = new Hammer(s.getDOMNode())
return s.getHammer()
}
addOutsideListener (listener) {
const s = this,
hammer = s.getHammer()
hammer.on('tap', listener)
}
removeOutsideListener (listener) {
const s = this,
hammer = s.getHammer()
hammer.off('tap', listener)
}
}
Object.assign(BodyTapRecognizer, {
singleton: new BodyTapRecognizer()
})
/** @lends ApOutsideMixin */
let ApOutsideMixin = {
// --------------------
// Custom
// --------------------
$apOutsideMixed: true,
statics: {},
handleTapForOutside(e) {
const s = this,
{ props } = s,
node = ReactDOM.findDOMNode(s)
if (!node) {
return
}
let contained = node.contains(e.target)
if (!contained) {
s.outsideDidTap(e)
if (props.onOutside) {
props.onOutside(e)
}
}
},
// --------------------
// Specs
// --------------------
propTypes: {
onOutside: types.func
},
// --------------------
// Lifecycle
// --------------------
componentWillMount () {
const s = this
let noop = () => undefined
defaults(s, {
outsideDidTap: noop
})
},
componentDidMount () {
const s = this
BodyTapRecognizer.singleton.addOutsideListener(s.handleTapForOutside)
},
componentWillUnmount () {
const s = this
BodyTapRecognizer.singleton.removeOutsideListener(s.handleTapForOutside)
}
}
export default Object.freeze(ApOutsideMixin)