predix-ui
Version:
px-* web components as React styled components
272 lines (245 loc) • 6.14 kB
JavaScript
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Arrow, Popper } from 'react-popper';
import styled from 'styled-components';
import { mapToCssModules, getTarget } from '../utils';
const PopoverInner = styled.div`
top: 0;
left: 0;
z-index: 1060;
max-width: 276px;
font-style: normal;
font-weight: 400;
line-height: 1.5;
text-align: left;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-break: normal;
word-spacing: normal;
white-space: normal;
line-break: auto;
font-size: 1rem;
word-wrap: break-word;
background-clip: padding-box;
border: 1px solid var(--px-popover-border-color, #000);
background: var(--px-popover-background-color, #fff);
color: var(--px-popover-text-color, #000);
box-shadow: 0 1px 3px 0 var(--px-popover-shadow-color, #000);
position: absolute;
display: block;
`;
PopoverInner.displayName = 'PopoverInner';
class PopperContent extends React.Component {
constructor(props) {
super(props);
this.handlePlacementChange = this.handlePlacementChange.bind(this);
this.setTargetNode = this.setTargetNode.bind(this);
this.getTargetNode = this.getTargetNode.bind(this);
this.state = {};
}
getChildContext() {
return {
popperManager: {
setTargetNode: this.setTargetNode,
getTargetNode: this.getTargetNode
}
};
}
componentDidMount() {
this.handleProps();
}
componentDidUpdate(prevProps) {
if (this.props.isOpen !== prevProps.isOpen) {
this.handleProps();
} else if (this._element) {
// rerender
this.renderIntoSubtree();
}
}
componentWillUnmount() {
this.hide();
}
setTargetNode(node) {
this.targetNode = node;
}
getTargetNode() {
return this.targetNode;
}
getContainerNode() {
return getTarget(this.props.container);
}
handlePlacementChange(data) {
if (this.state.placement !== data.placement) {
this.setState({ placement: data.placement });
}
return data;
}
handleProps() {
if (this.props.container !== 'inline') {
if (this.props.isOpen) {
this.show();
} else {
this.hide();
}
}
}
hide() {
if (this._element) {
this.getContainerNode().removeChild(this._element);
ReactDOM.unmountComponentAtNode(this._element);
this._element = null;
}
}
show() {
this._element = document.createElement('div');
this.getContainerNode().appendChild(this._element);
this.renderIntoSubtree();
if (
this._element.childNodes &&
this._element.childNodes[0] &&
this._element.childNodes[0].focus
) {
this._element.childNodes[0].focus();
}
}
renderIntoSubtree() {
ReactDOM.unstable_renderSubtreeIntoContainer(
this,
this.renderChildren(),
this._element
);
}
renderChildren() {
const {
cssModule,
children,
isOpen,
flip,
target,
offset,
fallbackPlacement,
placementPrefix,
hideArrow,
className,
tag,
container,
modifiers,
...attrs
} = this.props;
const arrowClassName = mapToCssModules('arrow', cssModule);
const placement = (this.state.placement || attrs.placement).split('-')[0];
const popperClassName = mapToCssModules(
classNames(
className,
placementPrefix ? `${placementPrefix}-${placement}` : placement
),
this.props.cssModule
);
const extendedModifiers = {
offset: { offset },
flip: { enabled: flip, behavior: fallbackPlacement },
update: {
enabled: true,
order: 950,
fn: this.handlePlacementChange
},
...modifiers
};
/* return (
<Popper
modifiers={extendedModifiers}
{...attrs}
component={tag}
className={popperClassName}>
{({ popperProps, restProps }) => (
<PopoverInner {...popperProps} {...at}>
{children}
{!hideArrow &&
<Arrow>
{({ arrowProps, restProps }) => (
<PopoverArrow
className="popper__arrow"
{...arrowProps}
/>
)}
</Arrow>}
</PopoverInner>
)}
</Popper>
); */
return (
<Popper
modifiers={extendedModifiers}
{...attrs}
component={tag}
className={popperClassName}
>
{children}
{!hideArrow && <Arrow className={arrowClassName} />}
</Popper>
);
}
render() {
this.setTargetNode(getTarget(this.props.target));
if (this.props.container === 'inline') {
return this.props.isOpen ? this.renderChildren() : null;
}
return null;
}
}
PopperContent.childContextTypes = {
popperManager: PropTypes.object.isRequired
};
PopperContent.defaultProps = {
placement: 'auto',
hideArrow: false,
isOpen: false,
offset: 0,
fallbackPlacement: 'flip',
flip: true,
container: 'body',
modifiers: {},
cssModule: {},
children: null,
placementPrefix: null,
tag: 'div',
target: null,
className: null
};
PopperContent.propTypes = {
placement: PropTypes.oneOf([
'auto-start',
'auto',
'auto-end',
'top-start',
'top',
'top-end',
'right-start',
'right',
'right-end',
'bottom-end',
'bottom',
'bottom-start',
'left-end',
'left',
'left-start'
]),
hideArrow: PropTypes.bool,
isOpen: PropTypes.bool,
offset: PropTypes.number,
fallbackPlacement: PropTypes.string,
flip: PropTypes.bool,
placementPrefix: PropTypes.string,
className: PropTypes.string,
container: PropTypes.string,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
tag: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
target: PropTypes.oneOfType([PropTypes.instanceOf(Element), Popper]),
cssModule: PropTypes.objectOf(PropTypes.string),
modifiers: PropTypes.objectOf(PropTypes.string)
};
export default PopperContent;