Add tooltips capabilities to buttons (#5562)
Add tooltips for buttons on ActionBar if text not visible
This commit is contained in:
committed by
Jaco Greeff
parent
3e86b2e666
commit
eff4cde738
@@ -14,7 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||
|
||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
@@ -22,6 +24,9 @@ import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
import styles from './actionbar.css';
|
||||
|
||||
export default class Actionbar extends Component {
|
||||
buttons = {};
|
||||
buttonsTooltip = {};
|
||||
|
||||
static propTypes = {
|
||||
title: nodeOrStringProptype(),
|
||||
buttons: PropTypes.array,
|
||||
@@ -29,6 +34,30 @@ export default class Actionbar extends Component {
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
buttons: []
|
||||
};
|
||||
|
||||
state = {
|
||||
buttons: []
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.setButtons(this.props);
|
||||
|
||||
window.addEventListener('resize', this.checkButtonsTooltip);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('resize', this.checkButtonsTooltip);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!isEqual(this.props.buttons, nextProps.buttons)) {
|
||||
this.setButtons(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, className } = this.props;
|
||||
const classes = `${styles.actionbar} ${className}`;
|
||||
@@ -43,9 +72,9 @@ export default class Actionbar extends Component {
|
||||
}
|
||||
|
||||
renderButtons () {
|
||||
const { buttons } = this.props;
|
||||
const { buttons } = this.state;
|
||||
|
||||
if (!buttons || !buttons.length) {
|
||||
if (buttons.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -65,4 +94,109 @@ export default class Actionbar extends Component {
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
checkButtonsTooltip = () => {
|
||||
const buttonsTooltip = Object.keys(this.buttons)
|
||||
.reduce((buttonsTooltip, index) => {
|
||||
buttonsTooltip[index] = this.checkButtonTooltip(this.buttons[index]);
|
||||
return buttonsTooltip;
|
||||
}, {});
|
||||
|
||||
if (isEqual(buttonsTooltip, this.buttonsTooltip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.buttonsTooltip = buttonsTooltip;
|
||||
this.setButtons(this.props);
|
||||
}
|
||||
|
||||
checkButtonTooltip = (button) => {
|
||||
const { icon, text } = button;
|
||||
const iconBoundings = icon.getBoundingClientRect();
|
||||
const textBoundings = text.getBoundingClientRect();
|
||||
|
||||
// Visible if the bottom of the text is above the bottom of the
|
||||
// button (text is v-aligned on top)
|
||||
const isTextVisible = textBoundings.top + textBoundings.height < iconBoundings.top + iconBoundings.height;
|
||||
|
||||
return !isTextVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the icon and text nodes of a Button
|
||||
* (and SVG/IMG for the icon next to a span node)
|
||||
*/
|
||||
getIconAndTextNodes (element) {
|
||||
if (!element || !element.children || element.children.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const children = Array.slice(element.children);
|
||||
const text = children.find((child) => child.nodeName.toLowerCase() === 'span');
|
||||
const icon = children.find((child) => {
|
||||
const nodeName = child.nodeName.toLowerCase();
|
||||
|
||||
return nodeName === 'svg' || nodeName === 'img';
|
||||
});
|
||||
|
||||
if (icon && text) {
|
||||
return { icon, text };
|
||||
}
|
||||
|
||||
const result = children
|
||||
.map((child) => {
|
||||
return this.getIconAndTextNodes(child);
|
||||
})
|
||||
.filter((result) => result);
|
||||
|
||||
return result.length > 0
|
||||
? result[0]
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tooltip to all Buttons
|
||||
*/
|
||||
patchButton (element, extraProps) {
|
||||
if (element.type.displayName !== 'Button') {
|
||||
if (!element.props.children) {
|
||||
return element;
|
||||
}
|
||||
|
||||
const children = this.patchButton(element.props.children);
|
||||
|
||||
return React.cloneElement(element, {}, children);
|
||||
}
|
||||
|
||||
return React.cloneElement(element, extraProps);
|
||||
}
|
||||
|
||||
setButtons (props) {
|
||||
const buttons = props.buttons
|
||||
.filter((button) => button)
|
||||
.map((button, index) => {
|
||||
const ref = this.setButtonRef.bind(this, index);
|
||||
const showTooltip = this.buttonsTooltip[index];
|
||||
|
||||
return this.patchButton(button, { tooltip: showTooltip, ref });
|
||||
});
|
||||
|
||||
this.setState({ buttons });
|
||||
}
|
||||
|
||||
setButtonRef = (index, element) => {
|
||||
const node = ReactDOM.findDOMNode(element);
|
||||
const iconAndText = this.getIconAndTextNodes(node);
|
||||
|
||||
if (!iconAndText) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.buttons[index]) {
|
||||
this.buttonsTooltip[index] = this.checkButtonTooltip(iconAndText);
|
||||
this.setButtons(this.props);
|
||||
}
|
||||
|
||||
this.buttons[index] = iconAndText;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user