Better tooltips
parent
f1a3031a57
commit
fcc7902839
|
@ -1,10 +1,26 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import Portal from '../hoc/Portal';
|
||||
import classnames from 'classnames';
|
||||
import { themr } from 'react-css-themr';
|
||||
import { TOOLTIP } from '../identifiers.js';
|
||||
import events from '../utils/events';
|
||||
|
||||
const factory = (defaultTheme = {}) => {
|
||||
const Tooltip = (ComposedComponent) => {
|
||||
const defaults = {
|
||||
className: '',
|
||||
delay: 0,
|
||||
hideOnClick: true,
|
||||
theme: {}
|
||||
};
|
||||
|
||||
const tooltipFactory = (options = {}) => {
|
||||
const {
|
||||
className: defaultClassName,
|
||||
delay: defaultDelay,
|
||||
hideOnClick: defaultHideOnClick,
|
||||
theme: defaultTheme
|
||||
} = {...defaults, ...options};
|
||||
|
||||
return ComposedComponent => {
|
||||
class TooltippedComponent extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
|
@ -23,51 +39,98 @@ const factory = (defaultTheme = {}) => {
|
|||
};
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
tooltipDelay: 0,
|
||||
tooltipHideOnClick: true
|
||||
className: defaultClassName,
|
||||
tooltipDelay: defaultDelay,
|
||||
tooltipHideOnClick: defaultHideOnClick
|
||||
};
|
||||
|
||||
state = {
|
||||
active: false
|
||||
active: false,
|
||||
visible: false
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.refs.tooltip) {
|
||||
events.removeEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
|
||||
}
|
||||
}
|
||||
|
||||
activate (top, left) {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
this.setState({ visible: true });
|
||||
this.timeout = setTimeout(() => {
|
||||
this.setState({ active: true, top, left });
|
||||
}, this.props.tooltipDelay);
|
||||
}
|
||||
|
||||
deactivate () {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
if (this.state.active) {
|
||||
events.addEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
|
||||
this.setState({ active: false });
|
||||
} else if (this.state.visible) {
|
||||
this.setState({ visible: false });
|
||||
}
|
||||
}
|
||||
|
||||
onTransformEnd = (e) => {
|
||||
if (e.propertyName === 'transform') {
|
||||
events.removeEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
|
||||
this.setState({ visible: false });
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseEnter = (event) => {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() =>this.setState({active: true}), this.props.tooltipDelay);
|
||||
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);
|
||||
if (this.props.onMouseEnter) this.props.onMouseEnter(event);
|
||||
};
|
||||
|
||||
handleMouseLeave = (event) => {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
if (this.state.active) this.setState({active: false});
|
||||
this.deactivate();
|
||||
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
||||
};
|
||||
|
||||
handleClick = (event) => {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
if (this.props.tooltipHideOnClick) this.setState({active: false});
|
||||
if (this.props.tooltipHideOnClick) this.deactivate();
|
||||
if (this.props.onClick) this.props.onClick(event);
|
||||
};
|
||||
|
||||
render () {
|
||||
const {children, className, tooltip,
|
||||
tooltipDelay, tooltipHideOnClick, ...other} = this.props; //eslint-disable-line no-unused-vars
|
||||
const composedClassName = classnames(this.props.theme.tooltipWrapper, className);
|
||||
const tooltipClassName = classnames(this.props.theme.tooltip, {
|
||||
[this.props.theme.tooltipActive]: this.state.active
|
||||
});
|
||||
const { active, left, top, visible } = this.state;
|
||||
const {
|
||||
children,
|
||||
className,
|
||||
theme,
|
||||
tooltip,
|
||||
tooltipDelay, //eslint-disable-line no-unused-vars
|
||||
tooltipHideOnClick, //eslint-disable-line no-unused-vars
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ComposedComponent
|
||||
{...other}
|
||||
className={composedClassName}
|
||||
className={className}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
theme={theme}
|
||||
>
|
||||
{children ? children : null}
|
||||
<span data-react-toolbox="tooltip" className={tooltipClassName}>{tooltip}</span>
|
||||
{visible && (
|
||||
<Portal>
|
||||
<span
|
||||
ref="tooltip"
|
||||
children={tooltip}
|
||||
className={classnames(theme.tooltip, {[theme.tooltipActive]: active})}
|
||||
data-react-toolbox="tooltip"
|
||||
style={{ top, left }}
|
||||
/>
|
||||
</Portal>
|
||||
)}
|
||||
</ComposedComponent>
|
||||
);
|
||||
}
|
||||
|
@ -75,9 +138,6 @@ const factory = (defaultTheme = {}) => {
|
|||
|
||||
return themr(TOOLTIP, defaultTheme)(TooltippedComponent);
|
||||
};
|
||||
|
||||
return Tooltip;
|
||||
};
|
||||
|
||||
export default factory();
|
||||
export { factory as tooltipFactory };
|
||||
export default tooltipFactory;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { tooltipFactory } from './Tooltip.js';
|
||||
import tooltipFactory from './Tooltip.js';
|
||||
import theme from './theme.scss';
|
||||
|
||||
export default tooltipFactory(theme);
|
||||
const themedTooltipFactory = (options) => tooltipFactory({ ...options, theme });
|
||||
export default tooltipFactory({ theme });
|
||||
export { themedTooltipFactory as tooltipFactory };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Tooltip
|
||||
|
||||
A Tooltip is useful to show information on hover in any kind of component. We have a component that can be used as a **decorator** for any kind of component. You just have to take into account that the overflow in the component should be visible.
|
||||
A Tooltip is useful to show information on hover in any kind of component. We have a component that can be used as a **decorator** for any kind of component. Also, it's factory function is exposed so you can create your own decorator with specific properties.
|
||||
|
||||
<!-- example -->
|
||||
```jsx
|
||||
|
|
|
@ -3,14 +3,8 @@
|
|||
@import "../mixins";
|
||||
@import "./config";
|
||||
|
||||
.tooltipWrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
z-index: $z-index-higher;
|
||||
display: block;
|
||||
max-width: $tooltip-max-width;
|
||||
|
@ -22,6 +16,7 @@
|
|||
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;
|
||||
|
|
Loading…
Reference in New Issue