UNPKG

@zohodesk/popups

Version:

popups popupover modal dialog alert notifications popup handling for whole app

253 lines (204 loc) 7.05 kB
import React from 'react'; import ReactDOM from 'react-dom'; import hoistStatics from 'hoist-non-react-statics'; import viewPort from '../ViewPort'; let popups = {}; const closeGroupPopups = (groupName='global')=>{ let groupPopups = popups[groupName] || []; groupPopups.forEach(popup => { popup.state.isPopupOpen && resetPopupState(popup); }); }; const resetPopupState = (popup)=>{ popup.setState({ isPopupOpen: false, isPopupReady : false, position: 'bottom' }) } const resetAllPopupState = (popup,filterGroupName)=>{ Object.keys(popups).forEach(closeGroupPopups); } const Popup = (Component, group = 'global', customStyles)=>{ const popupStyles = Object.assign({},customStyles); group = group || 'global'; class Popup extends React.Component { constructor(props) { super(props); this.state = { isPopupOpen: (props.isPopupOpen || false) , isPopupReady : (props.isPopupReady || false) , position: props.position || 'bottom', fixedPosition : {} }; this.popupGroup = props.group || group; this.childGroup = props.childGroup; this.stopCloseEvent = this.stopCloseEvent.bind(this); this.documentKeyupHandler = this.documentKeyupHandler.bind(this); this.documentClickHandler = this.documentClickHandler.bind(this); this.togglePopup = this.togglePopup.bind(this); this.openPopupOnly = this.openPopupOnly.bind(this); this.closePopupOnly = this.closePopupOnly.bind(this); this.setRef = this.setRef.bind(this); //this.popupName = popupName; } setRef(el){ this.elementRef = el; } UNSAFE_componentWillReceiveProps(nextProps){ let state = null; if(typeof nextProps.isPopupOpen !== "undefined" && nextProps.isPopupOpen !== this.state.isPopupOpen ){ state = state || {}; state.isPopupOpen = nextProps.isPopupOpen; } if(typeof nextProps.isPopupReady !== "undefined" && nextProps.isPopupReady !== this.state.isPopupReady ){ state = state || {}; state.isPopupReady = nextProps.isPopupReady; } if(typeof nextProps.position !== "undefined" && nextProps.position !== this.state.position ){ state = state || {}; state.position = nextProps.position; } if(state){ this.setState(state); } } componentDidMount() { let groupPopups = popups[this.popupGroup] || []; groupPopups.push(this); popups[this.popupGroup] = groupPopups; if (groupPopups.length == 1) { global.document.addEventListener('click', this.documentClickHandler, false); global.document.addEventListener('keyup', this.documentKeyupHandler, false); } } componentWillUnmount() { popups = Object.keys(popups).reduce((res, groupName) => { if (groupName == this.popupGroup) { let groupPopups = popups[this.popupGroup]; let newGroupPopups = groupPopups.filter(popup => { return popup !== this; }); res[this.popupGroup] = newGroupPopups; } return res; }, popups); if (popups.length == 0) { global.document.removeEventListener('click', this.documentClickHandler); global.document.removeEventListener('keyup', this.documentKeyupHandler); } } togglePopup(e,dropElement,placeHoldeEl, isPopupOpen ) { this.stopCloseEvent(e); //Close the all other opes let groupPopups = popups[this.popupGroup]; groupPopups.forEach(popup => { if (popup != this) { popup.state.isPopupOpen && popup.setState({ isPopupOpen: false, position: 'bottom' }); } }); this.setState({ isPopupOpen: (isPopupOpen ? isPopupOpen : !this.state.isPopupOpen) , isPopupReady : false , position: 'bottom' },()=>{ if( !dropElement || !placeHoldeEl ){ return; }; requestAnimationFrame(()=>{ var frame = this.props.frameId ? document.getElementById(this.props.frameId) : document.getElementById("container"); let defaultPosition = this.props.defaultPosition || "bottom"; let betterPosition = viewPort.betterView( dropElement , placeHoldeEl , defaultPosition , frame); //Auto predict views if( betterPosition.view ){ this.setState({ isPopupReady : true , position: betterPosition.view, fixedPosition:betterPosition.fixedPosition }); } else{ this.setState({ isPopupReady : true , position: 'bottom' }); } }) }); } openPopupOnly(e,dropElement,placeHoldeEl) { this.togglePopup(e,dropElement,placeHoldeEl, true); /*this.stopCloseEvent(e); let groupPopups = popups[this.popupGroup]; groupPopups.forEach(popup => { if (popup != this) { popup.state.isPopupOpen && popup.setState({ isPopupOpen: false, position: 'bottom' }); } }); this.setState({ isPopupOpen: true , isPopupReady : false , position: 'bottom' },()=>{ if( !dropElement || !placeHoldeEl ){ return; }; requestAnimationFrame(()=>{ var frame = this.props.frameId ? document.getElementById(this.props.frameId) : document.getElementById("container"); let defaultPosition = this.props.defaultPosition || "bottomCenter"; let betterPosition = viewPort.betterView( dropElement , placeHoldeEl , defaultPosition , frame); //Auto predict views if( betterPosition.view ) { this.setState({ isPopupReady : true , position: betterPosition.view }); }else{ this.setState({ isPopupReady : true , position: defaultPosition }); } }) });*/ } closePopupOnly(e) { this.stopCloseEvent(e); resetPopupState(this); } documentClickHandler(e) { try { resetAllPopupState(); } catch (e) { console.error('error', e); } } documentKeyupHandler(e) { try { if (e.keyCode == 27) { resetAllPopupState(); } } catch (e) { console.log('error', e); } } stopCloseEvent(e) { e.stopPropagation && e.stopPropagation(); e.nativeEvent && e.nativeEvent.stopImmediatePropagation && e.nativeEvent.stopImmediatePropagation(); if(this.childGroup){ closeGroupPopups(this.childGroup); } } render() { let { position, isPopupOpen, isPopupReady } = this.state; let positionStyle = position ? popupStyles[position] || '' : ''; let arrowStyle = `${popupStyles.arrow} ${positionStyle}`; let popupOpenStyle = isPopupOpen ? `${popupStyles.isOpen} ${positionStyle}` : `${popupStyles.isHide}`; let popupReadyStyle = isPopupReady ? `${popupStyles.isReady}` : ''; let contentStyle = `${popupStyles.content} ${popupOpenStyle} ${popupReadyStyle}`; return ( <Component ref={this.setRef} {...this.props} {...this.state} openPopupOnly={this.openPopupOnly} closePopupOnly={this.closePopupOnly} togglePopup={this.togglePopup} isPopupOpen={this.state.isPopupOpen} removeClose={this.stopCloseEvent} popupStyle={{ arrow : arrowStyle, content : contentStyle }} popupStyles={popupStyles} /> ); } } return hoistStatics(Popup, Component); }; export default Popup; export { closeGroupPopups };