import * as React from 'react'; import { PureComponent } from 'react'; import * as ReactDOM from 'react-dom'; import OutsideClickable from '../OutsideClickable'; import Popper, { IPopper } from './popper'; import * as styles from './styles'; export interface Props { autoPosition?: boolean; align?: 'left' | 'center' | 'right'; onOutsideClick?: () => void; target?: HTMLElement; spacing?: 'none'; } export interface State { position?: string; transform?: string; } export default class Panel extends PureComponent { state: State = {}; popper?: IPopper; componentDidMount() { this.applyPopper(this.props); } componentWillUnmount() { if (this.popper) { this.popper.destroy(); this.popper = undefined; } } componentWillReceiveProps(nextProps: Props) { this.applyPopper(nextProps); } extractStyles = (state: any) => { if (state) { const left = Math.round(state.offsets.popper.left); const top = Math.round(state.offsets.popper.top); this.setState({ position: state.offsets.popper.position, transform: `translate3d(${left}px, ${top}px, 0px)`, }); } } private applyPopper(props: Props): void { const { content } = this.refs; const target = props.target || ReactDOM.findDOMNode(this).parentElement!; const boundary = this.findBoundary(target); if (target && boundary && content instanceof HTMLElement) { if (this.popper) { this.popper.destroy(); } this.popper = new Popper(target, content, { onCreate: this.extractStyles, onUpdate: this.extractStyles, placement: this.popperPlacement(), boundariesElement: boundary, modifiers: { applyStyle: { enabled: false, }, offset: { enabled: true, offset: '0 3px', }, hide: { enabled: false }, flip: { enabled: this.props.autoPosition, flipVariations: true, }, preventOverflow: { enabled: this.props.autoPosition, moveWithTarget: true, }, }, }); } } render() { const { position, transform } = this.state; const padding = this.props.spacing === 'none' ? '0' : undefined; return (
{this.props.children}
); } private findBoundary(elem: HTMLElement): HTMLElement { while (elem.parentElement) { elem = elem.parentElement; if (elem.dataset['editorChrome']) { return elem; } } return elem; } private popperPlacement() { const align = this.props.align || 'left'; switch (align) { case 'left': return 'bottom-start'; case 'center': return 'bottom'; case 'right': return 'bottom-end'; } } }