New approach for Tooltips
parent
de921b0438
commit
1c57370651
|
@ -2,9 +2,7 @@ import React from 'react';
|
|||
import ClassNames from 'classnames';
|
||||
import FontIcon from '../font_icon';
|
||||
import Ripple from '../ripple';
|
||||
import Tooltip from '../tooltip';
|
||||
import style from './style';
|
||||
import events from '../utils/events';
|
||||
|
||||
class Button extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -22,8 +20,6 @@ class Button extends React.Component {
|
|||
primary: React.PropTypes.bool,
|
||||
raised: React.PropTypes.bool,
|
||||
ripple: React.PropTypes.bool,
|
||||
tooltip: React.PropTypes.string,
|
||||
tooltipDelay: React.PropTypes.number,
|
||||
type: React.PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -39,15 +35,17 @@ class Button extends React.Component {
|
|||
};
|
||||
|
||||
handleMouseDown = (event) => {
|
||||
events.pauseEvent(event);
|
||||
if (this.refs.ripple) this.refs.ripple.start(event);
|
||||
if (this.props.onMouseDown) this.props.onMouseDown(event);
|
||||
};
|
||||
|
||||
handleMouseUp = () => {
|
||||
this.refs.button.blur();
|
||||
};
|
||||
|
||||
render () {
|
||||
const {accent, className, flat, floating, href, icon, inverse, label,
|
||||
mini, primary, raised, ripple,
|
||||
tooltip, tooltipDelay, ...others} = this.props;
|
||||
const { accent, children, className, flat, floating, href, icon,
|
||||
inverse, label, mini, primary, raised, ripple, ...others} = this.props;
|
||||
const element = href ? 'a' : 'button';
|
||||
const level = primary ? 'primary' : accent ? 'accent' : 'neutral';
|
||||
const shape = flat ? 'flat' : raised ? 'raised' : floating ? 'floating' : 'flat';
|
||||
|
@ -60,17 +58,19 @@ class Button extends React.Component {
|
|||
const props = {
|
||||
...others,
|
||||
href,
|
||||
ref: 'button',
|
||||
className: classes,
|
||||
disabled: this.props.disabled,
|
||||
onMouseDown: this.handleMouseDown,
|
||||
onMouseUp: this.handleMouseUp,
|
||||
'data-react-toolbox': 'button'
|
||||
};
|
||||
|
||||
return React.createElement(element, props,
|
||||
ripple ? <Ripple ref='ripple' /> : null,
|
||||
tooltip ? <Tooltip className={style.tooltip} delay={tooltipDelay} label={tooltip}/> : null,
|
||||
icon ? <FontIcon className={style.icon} value={icon}/> : null,
|
||||
label ? label : this.props.children
|
||||
label,
|
||||
children
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
|||
import ClassNames from 'classnames';
|
||||
import FontIcon from '../font_icon';
|
||||
import Ripple from '../ripple';
|
||||
import Tooltip from '../tooltip';
|
||||
import style from './style';
|
||||
import events from '../utils/events';
|
||||
|
||||
class Button extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -17,8 +15,6 @@ class Button extends React.Component {
|
|||
inverse: React.PropTypes.bool,
|
||||
primary: React.PropTypes.bool,
|
||||
ripple: React.PropTypes.bool,
|
||||
tooltip: React.PropTypes.string,
|
||||
tooltipDelay: React.PropTypes.number,
|
||||
type: React.PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -30,14 +26,16 @@ class Button extends React.Component {
|
|||
};
|
||||
|
||||
handleMouseDown = (event) => {
|
||||
events.pauseEvent(event);
|
||||
if (this.refs.ripple) this.refs.ripple.start(event);
|
||||
if (this.props.onMouseDown) this.props.onMouseDown(event);
|
||||
};
|
||||
|
||||
handleMouseUp = () => {
|
||||
this.refs.button.blur();
|
||||
};
|
||||
|
||||
render () {
|
||||
const {accent, children, className, href, icon, inverse,
|
||||
primary, ripple, tooltip, tooltipDelay, ...others} = this.props;
|
||||
const {accent, children, className, href, icon, inverse, primary, ripple, ...others} = this.props;
|
||||
const element = href ? 'a' : 'button';
|
||||
const level = primary ? 'primary' : accent ? 'accent' : 'neutral';
|
||||
const classes = ClassNames([style.toggle, style[level]], {[style.inverse]: inverse}, className);
|
||||
|
@ -45,15 +43,16 @@ class Button extends React.Component {
|
|||
const props = {
|
||||
...others,
|
||||
href,
|
||||
ref: 'button',
|
||||
className: classes,
|
||||
disabled: this.props.disabled,
|
||||
onMouseDown: this.handleMouseDown,
|
||||
onMouseUp: this.handleMouseUp,
|
||||
'data-react-toolbox': 'button'
|
||||
};
|
||||
|
||||
return React.createElement(element, props,
|
||||
ripple ? <Ripple ref='ripple' centered /> : null,
|
||||
tooltip ? <Tooltip className={style.tooltip} delay={tooltipDelay} label={tooltip}/> : null,
|
||||
icon ? <FontIcon className={style.icon} value={icon}/> : children
|
||||
);
|
||||
}
|
||||
|
|
|
@ -46,8 +46,6 @@ const TestButtons = () => (
|
|||
| `primary` | `false` | `false` | Indicates if the button should have primary color.|
|
||||
| `raised` | `Boolean` | `false` | If true, the button will have a raised look. |
|
||||
| `ripple` | `Boolean` | `true` | If true, component will have a ripple effect on click.|
|
||||
| `tooptip` | `String` | | The value will be shown as a tooltip when the button is hovered. |
|
||||
| `tooltipDelay` | `Number` | | Amount of time in milliseconds before the tooltip is visible.|
|
||||
|
||||
By default it will have neutral colors and a flat aspect even though the `flat` property is `false` by default. Also, some properties exclude others, for example a button cannot be `flat` and `raised` at the same time.
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
> span:not(.tooltip) {
|
||||
> span:not([data-react-toolbox="tooltip"]) {
|
||||
display: inline-block;
|
||||
line-height: $button-height;
|
||||
vertical-align: middle;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import ClassNames from 'classnames';
|
||||
import FontIcon from '../font_icon';
|
||||
import Tooltip from '../tooltip';
|
||||
import style from './style';
|
||||
|
||||
class Input extends React.Component {
|
||||
static propTypes = {
|
||||
children: React.PropTypes.any,
|
||||
className: React.PropTypes.string,
|
||||
disabled: React.PropTypes.bool,
|
||||
error: React.PropTypes.string,
|
||||
|
@ -19,8 +19,6 @@ class Input extends React.Component {
|
|||
onFocus: React.PropTypes.func,
|
||||
onKeyPress: React.PropTypes.func,
|
||||
required: React.PropTypes.bool,
|
||||
tooltip: React.PropTypes.string,
|
||||
tooltipDelay: React.PropTypes.number,
|
||||
type: React.PropTypes.string,
|
||||
value: React.PropTypes.any
|
||||
};
|
||||
|
@ -61,10 +59,11 @@ class Input extends React.Component {
|
|||
}
|
||||
|
||||
render () {
|
||||
const {disabled, error, icon, floating, label: labelText,
|
||||
maxLength, tooltip, tooltipDelay, type, value} = this.props;
|
||||
const { children, disabled, error, floating, icon,
|
||||
label: labelText, maxLength, multiline, type, value, ...others} = this.props;
|
||||
const length = maxLength && value ? value.length : 0;
|
||||
const labelClassName = ClassNames(style.label, {[style.fixed]: !floating});
|
||||
|
||||
const className = ClassNames(style.root, {
|
||||
[style.disabled]: disabled,
|
||||
[style.errored]: error,
|
||||
|
@ -72,15 +71,24 @@ class Input extends React.Component {
|
|||
[style.withIcon]: icon
|
||||
}, this.props.className);
|
||||
|
||||
const InputElement = React.createElement(multiline ? 'textarea' : 'input', {
|
||||
...others,
|
||||
className: ClassNames(style.input, {[style.filled]: value}),
|
||||
onChange: this.handleChange,
|
||||
ref: 'input',
|
||||
role: 'input',
|
||||
value
|
||||
});
|
||||
|
||||
return (
|
||||
<div data-react-toolbox='input' className={className}>
|
||||
{this.renderInput()}
|
||||
{InputElement}
|
||||
{icon ? <FontIcon className={style.icon} value={icon} /> : null}
|
||||
<span className={style.bar}></span>
|
||||
{labelText ? <label className={labelClassName}>{labelText}</label> : null}
|
||||
{error ? <span className={style.error}>{error}</span> : null}
|
||||
{maxLength ? <span className={style.counter}>{length}/{maxLength}</span> : null}
|
||||
{tooltip ? <Tooltip label={tooltip} delay={tooltipDelay}/> : null}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,8 +45,6 @@ class InputTest extends React.Component {
|
|||
| `onFocus` | `Function` | | Callback function that is fired when components is focused.|
|
||||
| `onKeyPress` | `Function` | | Callback function that is fired when a key is pressed.|
|
||||
| `required` | `Boolean` | `false` | If true, the html input has a required value.|
|
||||
| `tooptip` | `String` | | The value will be shown as a tooltip when the button is hovered. |
|
||||
| `tooltipDelay` | `Number` | | Amount of time in milliseconds before the tooltip is visible.|
|
||||
| `type` | `String` | `text` | Type of the input element. It can be a valid HTML5 input type|
|
||||
| `value` | `String` | | Current value of the input element.|
|
||||
|
||||
|
|
|
@ -1,69 +1,67 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ClassNames from 'classnames';
|
||||
import style from './style';
|
||||
|
||||
const HIDE_TIMEOUT = 100;
|
||||
|
||||
class Tooltip extends React.Component {
|
||||
const Tooltip = (ComposedComponent) => class extends React.Component {
|
||||
static propTypes = {
|
||||
children: React.PropTypes.any,
|
||||
className: React.PropTypes.string,
|
||||
delay: React.PropTypes.number,
|
||||
label: React.PropTypes.string
|
||||
onClick: React.PropTypes.func,
|
||||
onMouseEnter: React.PropTypes.func,
|
||||
onMouseLeave: React.PropTypes.func,
|
||||
tooltip: React.PropTypes.string,
|
||||
tooltipDelay: React.PropTypes.number,
|
||||
tooltipHideOnClick: React.PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
delay: 0
|
||||
tooltipDelay: 0,
|
||||
tooltipHideOnClick: true
|
||||
};
|
||||
|
||||
state = {
|
||||
active: false
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
const parent = ReactDOM.findDOMNode(this).parentNode;
|
||||
|
||||
if (parent.style.position !== 'relative' && parent.style.position !== 'absolute'){
|
||||
parent.style.position = 'relative';
|
||||
}
|
||||
|
||||
parent.onmouseover = this.handleParentMouseOver;
|
||||
parent.onmouseout = this.handleParentMouseOut;
|
||||
handleMouseEnter = () => {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() =>this.setState({active: true}), this.props.tooltipDelay);
|
||||
if (this.props.onMouseEnter) this.props.onMouseEnter();
|
||||
};
|
||||
|
||||
handleParentMouseOver = () => {
|
||||
setTimeout(() => {
|
||||
if (this.deferredHide) clearTimeout(this.deferredHide);
|
||||
const node = ReactDOM.findDOMNode(this);
|
||||
const parent = node.parentNode;
|
||||
const parentStyle = parent.currentStyle || window.getComputedStyle(parent);
|
||||
const offset = parseFloat(parentStyle['margin-bottom']) + parseFloat(parentStyle['padding-bottom']);
|
||||
const position = parent.getBoundingClientRect();
|
||||
|
||||
node.style.top = `${position.height - offset}px`;
|
||||
node.style.left = `${parseInt((position.width / 2) - (node.offsetWidth / 2))}px`;
|
||||
if (!this.state.active) this.setState({ active: true});
|
||||
}, this.props.delay);
|
||||
handleMouseLeave = () => {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
if (this.state.active) this.setState({active: false});
|
||||
if (this.props.onMouseLeave) this.props.onMouseLeave();
|
||||
};
|
||||
|
||||
handleParentMouseOut = () => {
|
||||
if (this.state.active) {
|
||||
this.deferredHide = setTimeout(() => { this.setState({active: false}); }, HIDE_TIMEOUT);
|
||||
}
|
||||
};
|
||||
handleClick = () => {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
if (this.props.tooltipHideOnClick) this.setState({active: false});
|
||||
if (this.props.onClick) this.props.onClick();
|
||||
}
|
||||
|
||||
render () {
|
||||
const className = ClassNames(style.root, {
|
||||
const {children, className, tooltip, tooltipDelay, tooltipHideOnClick, ...other} = this.props;
|
||||
const composedClassName = ClassNames(style.root, className);
|
||||
const tooltipClassName = ClassNames(style.tooltip, {
|
||||
[style.active]: this.state.active
|
||||
}, this.props.className);
|
||||
});
|
||||
|
||||
return (
|
||||
<span data-react-toolbox='tooltip' className={className}>
|
||||
{this.props.label}
|
||||
</span>
|
||||
<ComposedComponent
|
||||
{...other}
|
||||
className={composedClassName}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
{children ? children : null}
|
||||
<span data-react-toolbox="tooltip" className={tooltipClassName}>{tooltip}</span>
|
||||
</ComposedComponent>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Tooltip;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
$tooltip-background: rgba(97,97,97,.9);
|
||||
$tooltip-margin: 0.5 * $unit;
|
||||
$tooltip-border-radius: .2 * $unit;
|
||||
$tooltip-color: #fff;
|
||||
$tooltip-font-size: $unit;
|
||||
$tooltip-max-width: 17 * $unit;
|
||||
$tooltip-padding: .8 * $unit;
|
||||
$tooltip-animation-duration: 2000ms;
|
||||
$tooltip-background: rgba(97,97,97,.9) !default;
|
||||
$tooltip-margin: 0.5 * $unit !default;
|
||||
$tooltip-border-radius: .2 * $unit !default;
|
||||
$tooltip-color: #fff !default;
|
||||
$tooltip-font-size: $unit !default;
|
||||
$tooltip-max-width: 17 * $unit !default;
|
||||
$tooltip-padding: .8 * $unit !default;
|
||||
$tooltip-animation-duration: 200ms !default;
|
||||
|
|
|
@ -1,24 +1,30 @@
|
|||
# Tooltip
|
||||
|
||||
A tooltip is Useful for show information on hover in any kind of component. Out of the box react-toolbox offers you a property `tooltip` in the component `<Button>`.
|
||||
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.
|
||||
|
||||
<!-- example -->
|
||||
```jsx
|
||||
import Button from 'react-toolbox/lib/button';
|
||||
import Tooltip from 'react-toolbox/lib/tooltip';
|
||||
|
||||
const TooltipButton = Tooltip(Button);
|
||||
const TooltipInput = Tooltip(Input);
|
||||
|
||||
const TooltipTest = () => (
|
||||
<div>
|
||||
<p>Lorem ipsum dolor sit amet, <strong>consectetur<Tooltip label='This is a auto show tooltip' /></strong> adipiscing elit.</p>
|
||||
<Button label='Button with tooltip' raised accent tooltip='This is a tooltip by property' />
|
||||
<TooltipButton label='Bookmark' icon='bookmark' raised primary tooltip='Bookmark Tooltip' tooltipDelay={1000} />
|
||||
<TooltipButton icon='add' floating accent tooltip='Floating Tooltip' />
|
||||
<TooltipInput tooltip='lorem ipsum...' />
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
In any component you decorate with the Tooltip you'd get some additional props:
|
||||
|
||||
| Name | Type | Default | Description|
|
||||
|:-----|:-----|:-----|:-----|
|
||||
| `className` | `String` | `''` | Set a class to style the Component.|
|
||||
| `delay` | `Number` | | Amount of time in miliseconds before the tooltip is visible.|
|
||||
| `label` | `String` | | The text string to use for the tooltip.|
|
||||
| `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. |
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
@import "./config";
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
z-index: $z-index-higher;
|
||||
display: block;
|
||||
max-width: $tooltip-max-width;
|
||||
|
@ -17,28 +23,10 @@
|
|||
text-transform: none;
|
||||
background: $tooltip-background;
|
||||
border-radius: $tooltip-border-radius;
|
||||
transform: scale(0);
|
||||
transform-origin: top center;
|
||||
animation-duration: $tooltip-animation-duration;
|
||||
animation-timing-function: $animation-curve-default;
|
||||
animation-iteration-count: 1;
|
||||
animation-direction: forwards;
|
||||
transition: $animation-curve-default $tooltip-animation-duration transform;
|
||||
transform: scale(0) translateX(-50%);
|
||||
transform-origin: top left;
|
||||
&.active {
|
||||
animation-name: tooltip-animation;
|
||||
}
|
||||
&.large {
|
||||
padding: 2 * $tooltip-padding;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
@keyframes tooltip-animation {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
10%, 99% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
transform: scale(1) translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const TooltipButton = Tooltip(Button);
|
||||
const TooltipInput = Tooltip(Input);
|
||||
|
||||
const TooltipTest = () => (
|
||||
<div>
|
||||
<p>Lorem ipsum dolor sit amet, <strong>consectetur<Tooltip label='This is a auto show tooltip' delay={500}/></strong> adipiscing elit.</p>
|
||||
<Button label='Button with tooltip' raised accent tooltip='This is a tooltip by property' tooltipDelay={100}/>
|
||||
<TooltipButton label='Bookmark' icon='bookmark' raised primary tooltip='Bookmark Tooltip' tooltipDelay={1000} />
|
||||
<TooltipButton icon='add' floating accent tooltip='Floating Tooltip' />
|
||||
<TooltipInput tooltip='lorem ipsum...' />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@ const ButtonTest = () => (
|
|||
<IconButton icon='favorite' inverse />
|
||||
<IconButton icon='favorite' />
|
||||
<IconButton icon='favorite' disabled />
|
||||
<IconButton primary tooltip='Bookmark Tooltip' tooltipDelay={1000}>
|
||||
<GithubIcon/>
|
||||
</IconButton>
|
||||
<IconButton primary><GithubIcon/></IconButton>
|
||||
<Button icon='add' label='Add this' flat primary />
|
||||
<Button icon='add' label='Add this' flat disabled />
|
||||
</section>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import Button from '../../components/button';
|
||||
import Dialog from '../../components/dialog';
|
||||
import Tooltip from '../../components/tooltip';
|
||||
|
||||
class DialogTest extends React.Component {
|
||||
state = {
|
||||
|
@ -31,7 +30,7 @@ class DialogTest extends React.Component {
|
|||
title="Use Google's location service?"
|
||||
onOverlayClick={this.handleToggle}
|
||||
>
|
||||
<p>Let Google help apps <strong><Tooltip label='location' />determine location</strong>. This means sending anonymous location data to Google, even when no apps are running.</p>
|
||||
<p>Let Google help apps <strong>determine location</strong>. This means sending anonymous location data to Google, even when no apps are running.</p>
|
||||
</Dialog>
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -3,17 +3,19 @@ import Button from '../../components/button';
|
|||
import Input from '../../components/input';
|
||||
import Tooltip from '../../components/tooltip';
|
||||
|
||||
const TooltipButton = Tooltip(Button);
|
||||
const TooltipInput = Tooltip(Input);
|
||||
const TooltipStrong = Tooltip(({children, ...other}) => <strong {...other}>{children}</strong>);
|
||||
|
||||
const TooltipTest = () => (
|
||||
<section>
|
||||
<h5>Tooltip</h5>
|
||||
<p>Give information on :hover</p>
|
||||
<Button label='Bookmark' icon='bookmark' raised primary tooltip='Bookmark Tooltip' tooltipDelay={1000} />
|
||||
<Button icon='add' floating accent tooltip='Floating Tooltip'/>
|
||||
<Button icon='add' floating disabled tooltip='Floating can not be shown' />
|
||||
<Input tooltip='lorem ipsum...' />
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, <strong>consectetur<Tooltip label='This is a auto show tooltip' delay={300} /></strong> adipiscing elit.
|
||||
</p>
|
||||
<TooltipButton label='Bookmark' icon='bookmark' raised primary tooltip='Bookmark Tooltip' tooltipDelay={1000} />
|
||||
<TooltipButton icon='add' floating accent tooltip='Floating Tooltip' />
|
||||
<TooltipButton icon='add' floating disabled tooltip='Floating can not be shown' />
|
||||
<TooltipInput tooltip='lorem ipsum...' />
|
||||
<p>Lorem ipsum dolor sit amet, <TooltipStrong tooltip='This is a auto show tooltip'>consectetur</TooltipStrong> adipiscing elit.</p>
|
||||
</section>
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue