Repository URL to install this package:
|
Version:
3.0.4 ▾
|
import React from 'react'
import { MouseEvent, ReactChild } from 'react'
import toClassName from 'classnames'
import { isFunction } from 'exotic'
import { ClickBoundaryProps, ClickBoundaryRef } from './typings'
import { onClick } from './_deps'
// for the sake of seeing the props added
// const ariaLabel = props['aria-label'] || ''
// props['aria-label'] =
// ariaLabel +
// ' when this element is active and you click outside of it, it loses focus in a visual way'
class ClickBoundary extends React.PureComponent<ClickBoundaryProps> {
isListening: boolean = false
isActive: boolean = false
dom: ClickBoundaryRef
/**
* only happens in browser, `willMount` in server
*/
subscribeListener() {
if (this.isActive && !this.isListening) {
// console.debug('[click boundary] subscribing')
// @note - document.body
window.addEventListener('click', this.handleClick)
window.addEventListener('ontouchstart', this.handleClick)
this.isListening = true
}
}
/**
* only happens browser
*/
unSubscribeListener() {
if (this.isListening) {
// console.debug('[click boundary] unsubscribing')
// remember to remove all events to avoid memory leaks
window.removeEventListener('click', this.handleClick)
window.removeEventListener('ontouchstart', this.handleClick)
this.isListening = false
}
}
/**
* @todo update with forwardRef
*/
setRef = (dom: Element | any) => {
this.dom = dom
if (isFunction(this.props.innerRef)) {
this.props.innerRef(dom)
}
}
handleMouseEnter = (event: MouseEvent<any>) => {
this.isActive = false
// this.state.focus()
// console.log(
// 'handleMouseEnter event:',
// 'isActive:',
// this.state.isActive,
// 'isFocused:',
// this.state.isFocused
// )
}
handleMouseLeave = (event: MouseEvent<any>) => {
this.isActive = true
// this.state.blur()
// this.state.setActive(true)
// console.debug(
// '[ClickBoundary] handleMouseLeave',
// 'isActive',
// this.state.isActive,
// 'isFocused:',
// this.state.isFocused
// )
this.subscribeListener()
}
handleClick = (event: MouseEvent<any> | any) => {
// console.debug('[ClickBoundary] handleClick ' + this.props.className)
onClick(event, this.props, this.dom)
this.isActive = false
// this.state.setActive(false)
this.unSubscribeListener()
}
renderChildren(children: any, attributes: any) {
const onChild = (child: ReactChild) => {
// 2. safety
if (typeof child === 'string' || typeof child === 'number') {
return child
}
// 3. merge props
const props = {
...attributes,
...child.props,
}
return React.cloneElement(child, props)
}
return React.Children.map(children, onChild)
}
render() {
const {
className,
children,
onClickOutside,
nowrap,
...remainingProps
} = this.props
const attributes = {
...remainingProps,
isActive: this.isActive,
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
className: toClassName('click-boundary', className),
}
if (nowrap) {
return this.renderChildren(children, attributes)
} else {
return (
<div {...attributes} ref={this.setRef}>
{this.props.children}
</div>
)
}
}
}
export default ClickBoundary
export { ClickBoundary }