Javi Velasco 2016-08-07 20:51:31 +02:00
parent f250f6fa95
commit 041e00c741
3 changed files with 118 additions and 23 deletions

View File

@ -4,11 +4,22 @@ import classnames from 'classnames';
import { themr } from 'react-css-themr';
import { TOOLTIP } from '../identifiers.js';
import events from '../utils/events';
import utils from '../utils/utils';
const POSITION = {
BOTTOM: 'bottom',
HORIZONTAL: 'horizontal',
LEFT: 'left',
RIGHT: 'right',
TOP: 'top',
VERTICAL: 'vertical'
};
const defaults = {
className: '',
delay: 0,
hideOnClick: true,
position: POSITION.VERTICAL,
theme: {}
};
@ -17,6 +28,7 @@ const tooltipFactory = (options = {}) => {
className: defaultClassName,
delay: defaultDelay,
hideOnClick: defaultHideOnClick,
position: defaultPosition,
theme: defaultTheme
} = {...defaults, ...options};
@ -35,17 +47,20 @@ const tooltipFactory = (options = {}) => {
}),
tooltip: PropTypes.string,
tooltipDelay: PropTypes.number,
tooltipHideOnClick: PropTypes.bool
tooltipHideOnClick: PropTypes.bool,
tooltipPosition: PropTypes.oneOf(Object.keys(POSITION).map(key => POSITION[key]))
};
static defaultProps = {
className: defaultClassName,
tooltipDelay: defaultDelay,
tooltipHideOnClick: defaultHideOnClick
tooltipHideOnClick: defaultHideOnClick,
tooltipPosition: defaultPosition
};
state = {
active: false,
position: this.props.tooltipPosition,
visible: false
};
@ -55,9 +70,9 @@ const tooltipFactory = (options = {}) => {
}
}
activate (top, left) {
activate ({ top, left, position }) {
if (this.timeout) clearTimeout(this.timeout);
this.setState({ visible: true });
this.setState({ visible: true, position });
this.timeout = setTimeout(() => {
this.setState({ active: true, top, left });
}, this.props.tooltipDelay);
@ -73,6 +88,55 @@ const tooltipFactory = (options = {}) => {
}
}
getPosition (element) {
const { tooltipPosition } = this.props;
if (tooltipPosition === POSITION.HORIZONTAL) {
const origin = element.getBoundingClientRect();
const { width: ww } = utils.getViewport();
const toRight = origin.left < ((ww / 2) - origin.width / 2);
return toRight ? POSITION.RIGHT : POSITION.LEFT;
} else if (tooltipPosition === POSITION.VERTICAL) {
const origin = element.getBoundingClientRect();
const { height: wh } = utils.getViewport();
const toBottom = origin.top < ((wh / 2) - origin.height / 2);
return toBottom ? POSITION.BOTTOM : POSITION.TOP;
} else {
return tooltipPosition;
}
}
calculatePosition (element) {
const position = this.getPosition(element);
const { top, left, height, width } = element.getBoundingClientRect();
const xOffset = window.scrollX || window.pageXOffset;
const yOffset = window.scrollY || window.pageYOffset;
if (position === POSITION.BOTTOM) {
return {
top: top + height + yOffset,
left: left + (width / 2) + xOffset,
position
};
} else if (position === POSITION.TOP) {
return {
top: top + yOffset,
left: left + (width / 2) + xOffset,
position
};
} else if (position === POSITION.LEFT) {
return {
top: top + (height / 2) + yOffset,
left: left + xOffset,
position
};
} else if (position === POSITION.RIGHT) {
return {
top: top + (height / 2) + yOffset,
left: left + width + xOffset,
position
};
}
}
onTransformEnd = (e) => {
if (e.propertyName === 'transform') {
events.removeEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
@ -81,10 +145,7 @@ const tooltipFactory = (options = {}) => {
};
handleMouseEnter = (event) => {
const yOffset = window.scrollY || window.pageYOffset;
const xOffset = window.scrollX || window.pageXOffset;
const { top, left, height, width } = event.target.getBoundingClientRect();
this.activate(top + height + yOffset, left + (width / 2) + xOffset);
this.activate(this.calculatePosition(event.target));
if (this.props.onMouseEnter) this.props.onMouseEnter(event);
};
@ -99,7 +160,8 @@ const tooltipFactory = (options = {}) => {
};
render () {
const { active, left, top, visible } = this.state;
const { active, left, top, position, visible } = this.state;
const positionClass = `tooltip${position.charAt(0).toUpperCase() + position.slice(1)}`;
const {
children,
className,
@ -107,9 +169,15 @@ const tooltipFactory = (options = {}) => {
tooltip,
tooltipDelay, //eslint-disable-line no-unused-vars
tooltipHideOnClick, //eslint-disable-line no-unused-vars
tooltipPosition, //eslint-disable-line no-unused-vars
...other
} = this.props;
const _className = classnames(theme.tooltip, {
[theme.tooltipActive]: active,
[theme[positionClass]]: theme[positionClass]
});
return (
<ComposedComponent
{...other}
@ -122,13 +190,9 @@ const tooltipFactory = (options = {}) => {
{children ? children : null}
{visible && (
<Portal>
<span
ref="tooltip"
children={tooltip}
className={classnames(theme.tooltip, {[theme.tooltipActive]: active})}
data-react-toolbox="tooltip"
style={{ top, left }}
/>
<span ref="tooltip" className={_className} data-react-toolbox="tooltip" style={{top, left}}>
<span className={theme.tooltipInner}>{tooltip}</span>
</span>
</Portal>
)}
</ComposedComponent>

View File

@ -37,11 +37,16 @@ In any component you decorate with the Tooltip you'd get some additional props:
| `tooltip` | `String` | | The text string to use for the tooltip.|
| `tooltipDelay` | `Number` | | Amount of time in miliseconds spent before the tooltip is visible.|
| `tooltipHideOnClick` | `Boolean` | `true` | If true, the Tooltip hides after a click in the host component.|
| `tooltipPosition` | `String` | `vertical` | Determines the position of the tooltip. It can be automatic with `vertical` and `horizontal` values or forced with `bottom`, `top`, `left` or `right`.|
## Theming
| Name | Description|
|:---------|:-----------|
| `tooltip` | Added to the tooltip element.|
| `tooltip` | Added to the tooltip element wrapper.|
| `tooltipActive` | Added to the root when the tooltip is active.|
| `tooltipWrapper` | Wrapper for the root element used to position the tooltip.|
| `tooltipBottom` | Added to the root in case the tooltip is being positioned at bottom.|
| `tooltipInner` | Added to the inner element which sets the background, font and rounded borders.|
| `tooltipLeft` | Added to the root in case the tooltip is being positioned at left.|
| `tooltipRight` | Added to the root in case the tooltip is being positioned at right.|
| `tooltipTop` | Added to the root in case the tooltip is being positioned at top.|

View File

@ -7,23 +7,49 @@
position: absolute;
z-index: $z-index-higher;
display: block;
padding: $tooltip-margin;
max-width: $tooltip-max-width;
padding: $tooltip-padding;
margin: $tooltip-margin 0;
font-family: Roboto, sans-serif;
font-size: $tooltip-font-size;
font-weight: $font-weight-bold;
line-height: $font-size-small;
color: $tooltip-color;
text-align: center;
pointer-events: none;
text-transform: none;
background: $tooltip-background;
border-radius: $tooltip-border-radius;
transition: $animation-curve-default $tooltip-animation-duration transform;
transform: scale(0) translateX(-50%);
transform-origin: top left;
&.tooltipActive {
transform: scale(1) translateX(-50%);
}
&.tooltipTop {
transform: scale(0) translateX(-50%) translateY(-100%);
&.tooltipActive {
transform: scale(1) translateX(-50%) translateY(-100%);
}
}
&.tooltipLeft {
transform: scale(0) translateX(-100%) translateY(-50%);
&.tooltipActive {
transform: scale(1) translateX(-100%) translateY(-50%);
}
}
&.tooltipRight {
transform: scale(0) translateX(0) translateY(-50%);
&.tooltipActive {
transform: scale(1) translateX(0) translateY(-50%);
}
}
}
.tooltipInner {
background: $tooltip-background;
border-radius: $tooltip-border-radius;
color: $tooltip-color;
display: block;
padding: $tooltip-padding;
}