4936e99f30
* Health endpoint. * Asynchronous health endpoint. * Configure time api URL via CLI. * Tests for TimeChecker. * Health indication on Status page. * Adding status indication to tab titles. * Add status to ParityBar. * Fixing lints. * Add health status on SyncWarning. * Fix health URL for embed. * Nicer messages. * Fix tests. * Fixing JS tests. * NTP time sync (#5956) * use NTP to check time drift * update time module documentation * replace time_api flag with ntp_server * fix TimeChecker tests * fix ntp-server flag usage * hide status tooltip if there's no message to show * remove TimeProvider trait * use Cell in FakeNtp test trait * share fetch client and ntp client cpu pool * Add documentation to public method. * Removing peer count from status. * Remove unknown upgrade status. * Send two time requests at the time. * Revert "Send two time requests at the time." This reverts commit f7b754b1155076a5a5d8fdafa022801fae324452. * Defer reporting time synchronization issues. * Fix tests. * Fix linting.
204 lines
5.1 KiB
JavaScript
204 lines
5.1 KiB
JavaScript
// 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 <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';
|
|
|
|
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 (
|
|
<Toolbar className={ classes }>
|
|
{ this.renderTitle() }
|
|
{ this.renderButtons() }
|
|
{ children }
|
|
</Toolbar>
|
|
);
|
|
}
|
|
|
|
renderButtons () {
|
|
const { buttons } = this.state;
|
|
|
|
if (buttons.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<ToolbarGroup className={ styles.toolbuttons }>
|
|
{ buttons }
|
|
</ToolbarGroup>
|
|
);
|
|
}
|
|
|
|
renderTitle () {
|
|
const { title } = this.props;
|
|
|
|
return (
|
|
<h3 className={ styles.tooltitle }>
|
|
{ title }
|
|
</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;
|
|
};
|
|
}
|