// Copyright 2015-2017 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . 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'; import styles from './actionbar.css'; export default class Actionbar extends Component { buttons = {}; buttonsTooltip = {}; static propTypes = { title: nodeOrStringProptype(), buttons: PropTypes.array, children: PropTypes.node, className: PropTypes.string, health: PropTypes.node }; 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}`; return ( { this.renderTitle() } { this.renderButtons() } { children } ); } renderButtons () { const { buttons } = this.state; if (buttons.length === 0) { return null; } return ( { buttons } ); } renderTitle () { const { title } = this.props; return (

{ title }

); } 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; }; }