UNPKG

2.85 kBJavaScriptView Raw
1import React from 'react';
2import Portal from 'react-portal';
3import PropTypes from 'prop-types';
4
5export default class OverflowExpander extends React.Component {
6 static propTypes = {
7 availableWidth: PropTypes.number.isRequired,
8 children: PropTypes.any,
9 }
10
11 constructor(props) {
12 super(props);
13
14 this.state = {
15 doesOverflow: false,
16 showPopover: false,
17 };
18 }
19
20 componentDidMount() {
21 this.setState({ //eslint-disable-line react/no-did-mount-set-state
22 doesOverflow: this.doesOverflow(),
23 });
24 }
25
26 componentDidUpdate(prevProps, prevState) {
27 const doesOverflow = this.doesOverflow();
28
29 if (prevState.doesOverflow !== doesOverflow) {
30 this.setState({ //eslint-disable-line react/no-did-update-set-state
31 doesOverflow,
32 });
33 }
34 }
35
36 doesOverflow = () => {
37 const size = this.refs.measure.clientWidth;
38 return size >= this.props.availableWidth;
39 }
40
41 handleMouseEnter = (e) => {
42 const clientRect = e.target.getBoundingClientRect();
43 const docWidth = document.body.getBoundingClientRect().width;
44 const leftSide = clientRect.left + this.props.availableWidth > docWidth / 2;
45 const arrowWidth = 11;
46
47 this.setState({
48 showPopover: true,
49 popoverAnchorLeft: leftSide ? 'auto' : clientRect.left + this.props.availableWidth,
50 popoverAnchorRight: leftSide ? docWidth - clientRect.left + arrowWidth : 'auto',
51 popoverAnchorTop: (clientRect.top + clientRect.bottom) / 2,
52 popoverSide: leftSide ? 'left' : 'right',
53 });
54 }
55
56 handleMouseLeave = () => {
57 this.setState({
58 showPopover: false,
59 });
60 }
61
62 render() {
63 return (
64 <span onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} style={{
65 display: 'inline-block',
66 width: this.props.availableWidth,
67 overflow: 'hidden',
68 textOverflow: 'ellipsis',
69 whiteSpace: 'nowrap',
70 }}>
71 <span ref="measure" style={{
72 position: 'absolute',
73 visibility: 'hidden',
74 }}>{this.props.children}</span>
75 <span>{this.props.children}</span>
76 <Portal isOpened={!!(this.state.doesOverflow && this.state.showPopover)}>
77 <div className={`popover fade ${this.state.popoverSide} in`} style={{
78 display: 'block',
79 position: 'absolute',
80 top: this.state.popoverAnchorTop,
81 left: this.state.popoverAnchorLeft,
82 right: this.state.popoverAnchorRight,
83 transform: 'translateY(-50%)',
84 backgroundColor: '#fff',
85 }}>
86 <div className="arrow" style={{top: '50%'}}></div>
87 <div className="popover-content" style={{wordWrap: 'break-word', overflow: 'hidden', marginRight: '14px'}}>{this.props.children}</div>
88 </div>
89 </Portal>
90 </span>
91 );
92 }
93}