Better TabBar #3240

This commit is contained in:
Nicolas Gotchac 2016-11-21 14:31:06 +01:00
parent ca5fd0b23d
commit a317e4fdbf
2 changed files with 164 additions and 76 deletions

View File

@ -23,6 +23,11 @@
.tabs { .tabs {
width: 100%; width: 100%;
position: relative; position: relative;
display: flex;
& > * {
flex: 1;
}
} }
.tabs button, .tabs button,
@ -38,6 +43,7 @@
button.tabactive, button.tabactive,
button.tabactive:hover { button.tabactive:hover {
color: white !important;
background: rgba(0, 0, 0, 0.25) !important; background: rgba(0, 0, 0, 0.25) !important;
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
} }

View File

@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar'; import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
import { Tabs, Tab } from 'material-ui/Tabs'; import { Tab as MUITab } from 'material-ui/Tabs';
import { Badge, Tooltip } from '../../../ui'; import { Badge, Tooltip } from '../../../ui';
@ -33,20 +33,138 @@ const TABMAP = {
deploy: 'contract' deploy: 'contract'
}; };
class Tab extends Component {
static propTypes = {
active: PropTypes.bool,
view: PropTypes.object,
children: PropTypes.node,
pendings: PropTypes.number,
onChange: PropTypes.func
};
shouldComponentUpdate (nextProps) {
return nextProps.active !== this.props.active ||
(nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
}
render () {
const { active, view, children } = this.props;
const label = this.getLabel(view);
return (
<MUITab
className={ active ? styles.tabactive : '' }
selected={ active }
icon={ view.icon }
label={ label }
onClick={ this.handleClick }
>
{ children }
</MUITab>
);
}
getLabel (view) {
const { label } = view;
if (view.id === 'signer') {
return this.renderSignerLabel(label);
}
if (view.id === 'status') {
return this.renderStatusLabel(label);
}
return this.renderLabel(label);
}
renderLabel (name, bubble) {
return (
<div className={ styles.label }>
{ name }
{ bubble }
</div>
);
}
renderSignerLabel (label) {
const { pendings } = this.props;
if (pendings) {
const bubble = (
<Badge
color='red'
className={ styles.labelBubble }
value={ pendings } />
);
return this.renderLabel(label, bubble);
}
return this.renderLabel(label);
}
renderStatusLabel (label) {
// const { isTest, netChain } = this.props;
// const bubble = (
// <Badge
// color={ isTest ? 'red' : 'default' }
// className={ styles.labelBubble }
// value={ isTest ? 'TEST' : netChain } />
// );
return this.renderLabel(label, null);
}
handleClick = () => {
const { onChange, view } = this.props;
onChange(view);
}
}
class TabBar extends Component { class TabBar extends Component {
static contextTypes = { static contextTypes = {
router: PropTypes.object.isRequired router: PropTypes.object.isRequired
} };
static propTypes = { static propTypes = {
views: PropTypes.array.isRequired,
hash: PropTypes.string.isRequired,
pending: PropTypes.array, pending: PropTypes.array,
isTest: PropTypes.bool, isTest: PropTypes.bool,
netChain: PropTypes.string, netChain: PropTypes.string
settings: PropTypes.object.isRequired };
}
static defaultProps = {
pending: []
};
state = { state = {
activeRoute: '/accounts' activeViewId: ''
};
setActiveView (props = this.props) {
const { hash, views } = props;
const view = views.find((view) => view.value === hash);
this.setState({ activeViewId: view.id });
}
componentWillMount () {
this.setActiveView();
}
componentWillReceiveProps (nextProps) {
if (nextProps.hash !== this.props.hash) {
this.setActiveView(nextProps);
}
}
shouldComponentUpdate (nextProps, nextState) {
return (nextProps.hash !== this.props.hash) ||
(nextProps.pending.length !== this.props.pending.length) ||
(nextState.activeViewId !== this.state.activeViewId);
} }
render () { render () {
@ -81,100 +199,64 @@ class TabBar extends Component {
} }
renderTabs () { renderTabs () {
const { settings } = this.props; const { views, pending } = this.props;
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1]; const { activeViewId } = this.state;
const hash = TABMAP[windowHash] || windowHash;
const items = Object.keys(settings.views) const items = views
.filter((id) => settings.views[id].fixed || settings.views[id].active) .map((view, index) => {
.map((id) => { const body = (view.id === 'accounts')
const view = settings.views[id]; ? (
let label = this.renderLabel(view.label); <Tooltip className={ styles.tabbarTooltip } text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view' />
let body = null; )
: null;
if (id === 'accounts') { const active = activeViewId === view.id;
body = (
<Tooltip className={ styles.tabbarTooltip } text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view' />
);
} else if (id === 'signer') {
label = this.renderSignerLabel(label);
} else if (id === 'status') {
label = this.renderStatusLabel(label);
}
return ( return (
<Tab <Tab
className={ hash === view.value ? styles.tabactive : '' } active={ active }
value={ view.value } view={ view }
icon={ view.icon } onChange={ this.onChange }
key={ id } key={ index }
label={ label } pendings={ pending.length }
onActive={ this.onActivate(view.route) }> >
{ body } { body }
</Tab> </Tab>
); );
}); });
return ( return (
<Tabs <div
className={ styles.tabs } className={ styles.tabs }
value={ hash }> onChange={ this.onChange }>
{ items } { items }
</Tabs>
);
}
renderLabel = (name, bubble) => {
return (
<div className={ styles.label }>
{ name }
{ bubble }
</div> </div>
); );
} }
renderSignerLabel = (label) => { onChange = (view) => {
const { pending } = this.props;
let bubble = null;
if (pending && pending.length) {
bubble = (
<Badge
color='red'
className={ styles.labelBubble }
value={ pending.length } />
);
}
return this.renderLabel(label, bubble);
}
renderStatusLabel = (label) => {
// const { isTest, netChain } = this.props;
// const bubble = (
// <Badge
// color={ isTest ? 'red' : 'default' }
// className={ styles.labelBubble }
// value={ isTest ? 'TEST' : netChain } />
// );
return this.renderLabel(label, null);
}
onActivate = (activeRoute) => {
const { router } = this.context; const { router } = this.context;
return (event) => { router.push(view.route);
router.push(activeRoute); this.setState({ activeViewId: view.id });
this.setState({ activeRoute });
};
} }
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { settings } = state; const { views } = state.settings;
return { settings }; const filteredViews = Object
.keys(views)
.filter((id) => views[id].fixed || views[id].active)
.map((id) => ({
...views[id],
id
}));
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
const hash = TABMAP[windowHash] || windowHash;
return { views: filteredViews, hash };
} }
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {