zarm-web
Version:
基于 React 的桌面端UI库
223 lines (201 loc) • 4.86 kB
JavaScript
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import classnames from 'classnames';
import Popper from './popper';
import Events from '../utils/events';
const directMap = {
top: 'top',
topLeft: 'top-start',
topRight: 'top-end',
right: 'right',
rightTop: 'right-start',
rightBottom: 'right-end',
bottom: 'bottom',
bottomLeft: 'bottom-start',
bottomRight: 'bottom-end',
left: 'left',
leftTop: 'left-start',
leftBottom: 'left-end'
};
class Popover extends Component {
constructor(props) {
super(props);
this.instance = void 0;
this.pop = void 0;
this.reference = void 0;
this.popper = void 0;
this.timer = void 0;
this.arrow = void 0;
this.state = {
visible: false
};
}
componentDidMount() {
const {
visible
} = this.state;
const reference = findDOMNode(this.reference); // eslint-disable-line
const {
trigger
} = this.props;
if (trigger === 'click') {
Events.on(reference, 'click', () => {
this.setState({
visible: !visible
});
});
Events.on(document, 'click', ({
target
}) => {
if (!this.instance || this.instance.contains(target) || !reference || reference.contains(target) || !this.pop || this.pop.contains(target) || !this.popper) {
return;
}
this.hidePop();
});
} else {
Events.on(reference, 'mouseenter', () => {
this.showPop();
});
Events.on(reference, 'mouseleave', () => {
this.hidePop();
});
Events.on(this.pop, 'mouseenter', () => {
this.showPop();
});
Events.on(this.pop, 'mouseleave', () => {
this.hidePop();
});
}
}
componentWillReceiveProps(nextProps) {
const {
visible
} = this.state;
if (visible !== nextProps.visible) {
this.setState({
visible: !!nextProps.visible
});
}
}
componentDidUpdate() {
const {
visible
} = this.state;
const {
direction
} = this.props;
const reference = findDOMNode(this.reference); // eslint-disable-line
if (visible) {
if (this.popper) {
this.popper.update();
} else {
if (this.arrow) {
this.arrow.setAttribute('x-arrow', '');
}
this.popper = new Popper(reference, this.pop, {
placement: directMap[direction]
});
}
} else {
if (this.popper) {
this.popper.destroy();
}
delete this.popper;
}
}
componentWillUnmount() {
if (this.popper) {
this.popper.destroy();
}
delete this.popper;
}
showPop() {
clearTimeout(this.timer);
this.setState({
visible: true
});
}
hidePop() {
const {
trigger
} = this.props;
if (trigger === 'click') {
this.setState({
visible: false
});
return;
}
this.timer = setTimeout(() => {
this.setState({
visible: false
});
}, 200);
}
render() {
const {
visible
} = this.state;
const {
children,
content,
prefixCls,
className,
radius,
mask,
onMaskClick
} = this.props;
const child = React.isValidElement(children) ? children : React.createElement("span", null, children);
const popContent = typeof content === 'function' ? content() : content;
const cls = classnames({
'ui-popover': true,
[className]: !!className
});
const contentCls = classnames({
[`${prefixCls}-content`]: true,
[`${prefixCls}-content-show`]: visible,
[`${prefixCls}-content-radius`]: !!radius
});
const maskCls = classnames({
[`${prefixCls}-mask`]: true,
[`${prefixCls}-mask-show`]: visible
});
return React.createElement("div", {
className: cls // tslint:disable-next-line:jsx-no-multiline-js
,
ref: instance => {
this.instance = instance;
}
}, mask ? React.createElement("div", {
className: maskCls,
onClick: onMaskClick
}) : null, React.createElement("div", {
className: contentCls // tslint:disable-next-line:jsx-no-multiline-js
,
ref: pop => {
this.pop = pop;
}
}, popContent, React.createElement("span", {
className: `${prefixCls}-arrow` // tslint:disable-next-line:jsx-no-multiline-js
,
ref: arrow => {
this.arrow = arrow;
}
})), React.cloneElement(child, {
ref: reference => {
this.reference = reference;
}
}));
}
}
Popover.defaultProps = {
prefixCls: 'ui-popover',
className: null,
visible: false,
trigger: 'click',
mask: false,
radius: true,
direction: 'bottomRight',
onMaskClick() {},
content: null
};
export default Popover;