Repository URL to install this package:
|
Version:
3.0.6-working.1 ▾
|
import React from 'react'
import toClassName from 'classnames'
import { createPortal } from 'react-dom'
import { isObj, isUndefined, isString } from 'exotic'
import {
removeElement,
getElementOrCreate,
appendToBodyIfRequired,
} from './deps'
import { PortalProps } from './typings'
/**
* @api https://reactjs.org/docs/portals.html
*/
class Portal extends React.PureComponent<PortalProps> {
isComponentMounted = false
defaultNode: HTMLElement | undefined
componentWillUnmount() {
this.isComponentMounted = false
if (isObj(this.defaultNode) && isObj(this.defaultNode.classList)) {
this.defaultNode.classList.remove('visible')
if (isString(this.props.id) === false) {
removeElement(this.defaultNode)
this.defaultNode = undefined
}
}
}
/**
* @see https://reactjs.org/docs/reconciliation.html
*/
componentDidMount() {
this.isComponentMounted = true
this.forceUpdate()
}
/**
* @deprecated
* @todo remove this, we can access using props...
*/
get className() {
return toClassName('portal', this.props.className, {
visible: this.props.isVisible,
hidden: this.props.isVisible === false,
})
}
/**
* @todo reduce reading & writing from the DOM here, bad @@perf
*/
render() {
/**
* for best rendering, should not render on server
*/
if (this.isComponentMounted === false) {
return null
}
// if we send an id, find it
if (isString(this.props.id)) {
this.defaultNode = getElementOrCreate(this.props.id)
this.defaultNode.className = this.className
this.defaultNode.setAttribute(
'aria-hidden',
String(!this.props.isVisible)
)
}
// otherwise, we could send a dom node
else if (!this.props.node && isUndefined(this.defaultNode)) {
this.defaultNode = document.createElement('div')
}
// if we had to create it, we need to append it
appendToBodyIfRequired(this.defaultNode!)
return createPortal(
this.props.children,
this.props.node || this.defaultNode
)
}
}
export { Portal }
export default Portal