-
Tom Robinson authoredTom Robinson authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
header.react.js 7.41 KiB
'use strict';
/*global setTimeout, clearTimeout*/
import ActionButton from './action_button.react';
import AddToDashboard from './add_to_dashboard.react';
import Icon from './icon.react';
import Popover from './popover.react';
import QueryModeToggle from './query_mode_toggle.react';
import Saver from './saver.react';
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
export default React.createClass({
displayName: 'QueryHeader',
propTypes: {
card: React.PropTypes.object.isRequired,
cardApi: React.PropTypes.func.isRequired,
dashboardApi: React.PropTypes.func.isRequired,
notifyCardChangedFn: React.PropTypes.func.isRequired,
setQueryModeFn: React.PropTypes.func.isRequired,
downloadLink: React.PropTypes.string
},
getInitialState: function() {
return {
origCard: JSON.stringify(this.props.card),
recentlySaved: false,
resetOrigCard: false
};
},
componentWillReceiveProps: function(nextProps) {
// pre-empt a card update via props
// we need this here for a specific case where we know the card will be changing
// and thus we need to reset our :origCard state BEFORE our next render cycle
if (this.state.resetOrigCard) {
this.setState({
origCard: JSON.stringify(nextProps.card),
recentlySaved: false,
resetOrigCard: false
});
}
},
cardIsNew: function() {
// a card is considered new if it has not ID associated with it
return (this.props.card.id === undefined);
},
cardIsDirty: function() {
// a card is considered dirty if ANY part of it has been changed
return (JSON.stringify(this.props.card) !== this.state.origCard);
},
resetStateOnTimeout: function() {
// clear any previously set timeouts then start a new one
clearTimeout(this.timeout);
var component = this;
this.timeout = setTimeout(function() {
if (component.isMounted()) {
component.setState({
recentlySaved: false
});
}
}.bind(component), 5000);
},
save: function() {
return this.saveCard(this.props.card);
},
saveCard: function(card) {
var component = this,
apiCall;
if (card.id === undefined) {
// creating a new card
apiCall = this.props.cardApi.create(card, function (newCard) {
if (component.isMounted()) {
component.props.notifyCardCreatedFn(newCard);
// update local state to reflect new card state
component.setState({
origCard: JSON.stringify(card),
recentlySaved: true
}, component.resetStateOnTimeout);
}
});
} else {
// updating an existing card
apiCall = this.props.cardApi.update(card, function (updatedCard) {
if (component.isMounted()) {
component.props.notifyCardUpdatedFn(updatedCard);
// update local state to reflect new card state
component.setState({
origCard: JSON.stringify(card),
recentlySaved: true
}, component.resetStateOnTimeout);
}
});
}
return apiCall.$promise;
},
setQueryMode: function(mode) {
// we need to update our dirty state here
var component = this;
this.setState({
resetOrigCard: true
}, function() {
component.props.setQueryModeFn(mode);
});
},
permissions: function() {
var text;
switch(this.props.card.public_perms) {
case 0:
text = 'Private';
break;
case 1:
text = 'Others can read';
break;
case 2:
text = 'Others can modify';
break;
default:
return 'Error';
}
return (
<span className="Badge Badge--permissions ml2">
{text}
</span>
);
},
render: function() {
var title = this.props.card.name || "What would you like to know?";
var editButton;
if (!this.cardIsNew()) {
editButton = (
<Saver
card={this.props.card}
saveFn={this.props.notifyCardChangedFn}
saveButtonText="Done"
className='inline-block ml1 link'
/>
);
}
var saveButton;
if (this.cardIsNew() && this.cardIsDirty()) {
// new cards get a custom treatment, like saving a new Excel document
saveButton = (
<Saver
card={this.props.card}
saveFn={this.saveCard}
buttonText="Save"
saveButtonText="Create card"
/>
);
} else if (this.cardIsDirty() || this.state.recentlySaved) {
// for existing cards we render a very simply ActionButton
saveButton = (
<ActionButton
actionFn={this.save}
className='Button Button--primary'
/>
);
}
// NOTE: we expect our component provider set this to something falsey if download not available
var downloadButton;
if (this.props.downloadLink) {
downloadButton = (
<a className="mx1" href={this.props.downloadLink} title="Download this data" target="_blank">
<Icon name='download'>
<Popover>
<span>Download data</span>
</Popover>
</Icon>
</a>
);
}
var queryModeToggle;
if (this.cardIsNew() && !this.cardIsDirty()) {
queryModeToggle = (
<QueryModeToggle
currentQueryMode={this.props.card.dataset_query.type}
setQueryModeFn={this.setQueryMode}
/>
);
}
return (
<div className="QueryHeader QueryBuilder-section flex align-center">
<div className="QueryHeader-details">
<h1 className="QueryName">{title}</h1>
{this.permissions()}
{editButton}
</div>
<div className="QueryHeader-actions">
<ReactCSSTransitionGroup transitionName="Transition-qb-section">
{saveButton}
</ReactCSSTransitionGroup>
<ReactCSSTransitionGroup transitionName="Transition-qb-section">
{downloadButton}
</ReactCSSTransitionGroup>
<AddToDashboard
card={this.props.card}
dashboardApi={this.props.dashboardApi}
/>
<ReactCSSTransitionGroup transitionName="Transition-qb-querybutton">
{queryModeToggle}
</ReactCSSTransitionGroup>
</div>
</div>
);
}
});