Split JS code by classes into different files
This commit is contained in:
parent
0e5f20d48c
commit
e2ad2739cd
|
@ -22,12 +22,23 @@
|
||||||
<script src="/js/format.js"></script>
|
<script src="/js/format.js"></script>
|
||||||
<script src="/js/http.js"></script>
|
<script src="/js/http.js"></script>
|
||||||
<script src="/js/colors.js"></script>
|
<script src="/js/colors.js"></script>
|
||||||
|
<script src="/js/storage.js"></script>
|
||||||
|
<script src="/js/svg_text_width.js"></script>
|
||||||
<script src="/js/svground.js"></script>
|
<script src="/js/svground.js"></script>
|
||||||
<script src="/jsx/build/charts/charts.js"></script>
|
<script src="/jsx/build/charts/svg_chart_mixin.js"></script>
|
||||||
<script src="/jsx/build/charts/data.js"></script>
|
<script src="/jsx/build/charts/chart_data_mixin.js"></script>
|
||||||
<script src="/jsx/build/charts/animation.js"></script>
|
<script src="/jsx/build/charts/chart_animation_mixin.js"></script>
|
||||||
<script src="/jsx/build/charts/selector.js"></script>
|
<script src="/jsx/build/charts/selector.js"></script>
|
||||||
<script src="/jsx/build/charts/bar_chart.js"></script>
|
<script src="/jsx/build/charts/bc/bar_chart.js"></script>
|
||||||
<script src="/jsx/build/charts/stacked_area_chart.js"></script>
|
<script src="/jsx/build/charts/bc/bar.js"></script>
|
||||||
<script src="/jsx/build/app.js"></script>
|
<script src="/jsx/build/charts/sac/stacked_area_chart.js"></script>
|
||||||
|
<script src="/jsx/build/charts/sac/area.js"></script>
|
||||||
|
<script src="/jsx/build/charts/sac/dot.js"></script>
|
||||||
|
<script src="/jsx/build/charts/sac/axis.js"></script>
|
||||||
|
<script src="/jsx/build/app/app.js"></script>
|
||||||
|
<script src="/jsx/build/app/menu.js"></script>
|
||||||
|
<script src="/jsx/build/app/dashboard.js"></script>
|
||||||
|
<script src="/jsx/build/app/info_block.js"></script>
|
||||||
|
<script src="/jsx/build/app/week_interval_selector.js"></script>
|
||||||
|
<script src="/jsx/build/start.js"></script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
var Storage = {
|
||||||
|
set: function(category, key, value) {
|
||||||
|
window.localStorage.setItem(category +'-'+ key, JSON.stringify(value));
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function(category, key) {
|
||||||
|
var val = window.localStorage.getItem(category +'-'+ key);
|
||||||
|
return val === null ? {} : JSON.parse(val);
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,17 +1,9 @@
|
||||||
var SVGChartMixin = {
|
|
||||||
calculateViewBoxWidth: function() {
|
|
||||||
this.setState({
|
|
||||||
canvasWidth: this.refs.svg.getDOMNode().offsetWidth
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var fontFamily = "'Open Sans', Helvetica, Arial, sans-serif",
|
var fontFamily = "'Open Sans', Helvetica, Arial, sans-serif",
|
||||||
fontSize = 16;
|
fontSize = 16;
|
||||||
|
|
||||||
function textWidth(str) {
|
function textWidth(str) {
|
||||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
|
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
text = document.createElementNS('http://www.w3.org/2000/svg', "text");
|
text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
|
||||||
svg.width = 500;
|
svg.width = 500;
|
||||||
svg.height = 500;
|
svg.height = 500;
|
311
app/jsx/app.jsx
311
app/jsx/app.jsx
|
@ -1,311 +0,0 @@
|
||||||
var Router = ReactRouter,
|
|
||||||
Link = Router.Link;
|
|
||||||
|
|
||||||
var Storage = {
|
|
||||||
set: function(category, key, value) {
|
|
||||||
window.localStorage.setItem(category +'-'+ key, JSON.stringify(value));
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function(category, key) {
|
|
||||||
var val = window.localStorage.getItem(category +'-'+ key);
|
|
||||||
return val === null ? {} : JSON.parse(val);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var App = React.createClass({
|
|
||||||
mixins: [Router.Navigation, Router.State],
|
|
||||||
|
|
||||||
orgsURL: '/api/orgs',
|
|
||||||
teamsURL: '/api/teams',
|
|
||||||
usersURL: '/api/users',
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
orgs: [],
|
|
||||||
org: null,
|
|
||||||
teams: [],
|
|
||||||
team: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.loadOrgs();
|
|
||||||
this.loadTeams();
|
|
||||||
this.loadUsers();
|
|
||||||
},
|
|
||||||
|
|
||||||
loadOrgs: function() {
|
|
||||||
getURL(this.orgsURL, {}, function(res) {
|
|
||||||
this.setState({orgs: res});
|
|
||||||
if (res !== null) {
|
|
||||||
for (var i = 0; i < res.length; i++) {
|
|
||||||
var org = res[i];
|
|
||||||
Storage.set('org', org.login, org);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
loadTeams: function() {
|
|
||||||
getURL(this.teamsURL, {org: this.getParams().org}, function(res) {
|
|
||||||
this.setState({teams: res});
|
|
||||||
if (res !== null) {
|
|
||||||
for (var i = 0; i < res.length; i++) {
|
|
||||||
var team = res[i];
|
|
||||||
Storage.set('team', team.name, team);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
loadUsers: function() {
|
|
||||||
getURL(this.usersURL, {org: this.getParams().org}, function(res) {
|
|
||||||
this.setState({users: res});
|
|
||||||
if (res !== null) {
|
|
||||||
for (var i = 0; i < res.length; i++) {
|
|
||||||
var user = res[i];
|
|
||||||
Storage.set('user', user.login, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<div className="master">
|
|
||||||
<div className="app" id="app">
|
|
||||||
<Menu orgs={this.state.orgs} teams={this.state.teams} />
|
|
||||||
<Router.RouteHandler />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Menu = React.createClass({
|
|
||||||
mixins: [Router.State],
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var renderOrg = function(org) {
|
|
||||||
return (
|
|
||||||
<li key={'org-'+ org.login} className="nav org">
|
|
||||||
<Link to="org"
|
|
||||||
params={{org: org.login}}
|
|
||||||
query={this.getQuery()}>
|
|
||||||
{org.login}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}.bind(this);
|
|
||||||
var renderTeam = function(team) {
|
|
||||||
return (
|
|
||||||
<li key={'team-'+ team.name} className="nav team">
|
|
||||||
<Link to="team"
|
|
||||||
params={{org: team.owner, team: team.name}}
|
|
||||||
query={this.getQuery()}>
|
|
||||||
{team.name}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}.bind(this);
|
|
||||||
return (
|
|
||||||
<div className="menu">
|
|
||||||
<ul>
|
|
||||||
<li className="empact">
|
|
||||||
<Link to="org" params={this.getParams()} className="logo-button">
|
|
||||||
<div className="logo e">e</div>
|
|
||||||
<div className="logo m">m</div>
|
|
||||||
<div className="logo p">p</div>
|
|
||||||
<div className="logo a">a</div>
|
|
||||||
<div className="logo c">c</div>
|
|
||||||
<div className="logo t">t</div>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li className="nav header">Organizations:</li>
|
|
||||||
{this.props.orgs.map(renderOrg)}
|
|
||||||
<li className="nav header">Teams:</li>
|
|
||||||
{this.props.teams.map(renderTeam)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Org = React.createClass({
|
|
||||||
render: function(){
|
|
||||||
return (
|
|
||||||
<Router.RouteHandler />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Dashboard = React.createClass({
|
|
||||||
mixins: [Router.State],
|
|
||||||
|
|
||||||
render: function(){
|
|
||||||
var p = this.getParams(),
|
|
||||||
infoImage, infoImageClass, infoTitle,
|
|
||||||
bcApi, bcItems,
|
|
||||||
sacApi, sacItems;
|
|
||||||
|
|
||||||
if (p.team) {
|
|
||||||
infoTitle = p.team +' Team';
|
|
||||||
infoImageClass = 'team';
|
|
||||||
bcApi = '/api/stat/teams/top';
|
|
||||||
bcItems = ['repo', 'user'],
|
|
||||||
sacApi = '/api/stat/teams/activity';
|
|
||||||
sacItems = ['user', 'repo'];
|
|
||||||
} else if (p.user) {
|
|
||||||
var info = Storage.get('user', p.user);
|
|
||||||
infoImage = info ? info.avatar_url : null;
|
|
||||||
infoTitle = info && info.name ? info.name : p.user;
|
|
||||||
bcApi = '/api/stat/users/top';
|
|
||||||
bcItems = ['repo'],
|
|
||||||
sacApi = '/api/stat/users/activity';
|
|
||||||
sacItems = ['repo'];
|
|
||||||
} else if (p.repo) {
|
|
||||||
infoTitle = p.repo;
|
|
||||||
infoImageClass = 'repo';
|
|
||||||
bcApi = '/api/stat/repos/top';
|
|
||||||
bcItems = ['user', 'team'],
|
|
||||||
sacApi = '/api/stat/repos/activity';
|
|
||||||
sacItems = ['user', 'team'];
|
|
||||||
} else {
|
|
||||||
var info = Storage.get('org', p.org);
|
|
||||||
infoImage = info.avatar_url;
|
|
||||||
infoTitle = info.login;
|
|
||||||
bcApi = '/api/stat/orgs/top';
|
|
||||||
bcItems = ['repo', 'team', 'user'],
|
|
||||||
sacApi = '/api/stat/orgs/activity';
|
|
||||||
sacItems = ['team', 'user', 'repo'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="content">
|
|
||||||
<InfoBlock image={infoImage} className={infoImageClass} title={infoTitle} />
|
|
||||||
<BarChart api={bcApi} params={this.getParams()} items={bcItems} />
|
|
||||||
<StackedAreaChart api={sacApi} params={this.getParams()} items={sacItems} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var NotFound = React.createClass({
|
|
||||||
render: function(){
|
|
||||||
return (
|
|
||||||
<div className="content">NOT FOUND :(</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var SelectOrg = React.createClass({
|
|
||||||
render: function(){
|
|
||||||
return (
|
|
||||||
<div className="content">Please select organization from the menu!</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var InfoBlock = React.createClass({
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<div className="info-block">
|
|
||||||
<div className={'img'+ (this.props.image ? '' : ' empty') +' '+ this.props.className}
|
|
||||||
style={this.props.image ? {backgroundImage: "url("+ this.props.image +")"} : null} />
|
|
||||||
<h1>{this.props.title}</h1>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var WeekIntervalSelector = React.createClass({
|
|
||||||
mixins: [ReactRouter.Navigation, ReactRouter.State],
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
var ms = 1000,
|
|
||||||
daySeconds = 86400,
|
|
||||||
weekSeconds = daySeconds*7,
|
|
||||||
today = new Date(),
|
|
||||||
sunday = new Date(today - daySeconds*ms*today.getDay()),
|
|
||||||
perfectSunday = new Date(Date.UTC(sunday.getFullYear(), sunday.getMonth(), sunday.getDate())),
|
|
||||||
lastWeek = perfectSunday.setHours(0)/ms,
|
|
||||||
firstWeek = lastWeek - 51*weekSeconds;
|
|
||||||
|
|
||||||
var weeks = [];
|
|
||||||
for (var i = lastWeek; i >= firstWeek; i -= weekSeconds) {
|
|
||||||
weeks.push(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
weeks: weeks.sort()
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
handleChange: function(thing, e) {
|
|
||||||
var params = this.getQuery();
|
|
||||||
params[thing.slice(0, 1)] = e.target.value/100;
|
|
||||||
this.transitionTo(document.location.pathname, null, params);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var daySeconds = 86400,
|
|
||||||
weekSeconds = daySeconds*7,
|
|
||||||
lastWeek = this.state.weeks[this.state.weeks.length-1],
|
|
||||||
from = (this.getQuery().f ? parseInt(this.getQuery().f, 10)*100 : lastWeek - 29*weekSeconds),
|
|
||||||
to = (this.getQuery().t ? parseInt(this.getQuery().t, 10)*100 : lastWeek);
|
|
||||||
|
|
||||||
var weeksBefore = _(this.state.weeks)
|
|
||||||
.filter(function(week) {
|
|
||||||
return week < to;
|
|
||||||
})
|
|
||||||
.reverse()
|
|
||||||
.value();
|
|
||||||
var weeksAfter = _(this.state.weeks)
|
|
||||||
.filter(function(week) {
|
|
||||||
return week > from;
|
|
||||||
})
|
|
||||||
.reverse()
|
|
||||||
.value();
|
|
||||||
|
|
||||||
var renderOption = function(ts) {
|
|
||||||
return (
|
|
||||||
<option key={ts} value={ts}>{formatDate(ts, true)}</option>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="week-selector">
|
|
||||||
<span>from</span>
|
|
||||||
<div ref="from" className="selector">
|
|
||||||
<em ref="label">{formatDate(from)}</em>
|
|
||||||
<select ref="select" value={from} onChange={this.handleChange.bind(this, 'from')}>
|
|
||||||
{weeksBefore.map(renderOption)}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<span>to</span>
|
|
||||||
<div ref="to" className="selector">
|
|
||||||
<em ref="label">{formatDate(to)}</em>
|
|
||||||
<select ref="select" value={to} onChange={this.handleChange.bind(this, 'to')}>
|
|
||||||
{weeksAfter.map(renderOption)}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var routes = [
|
|
||||||
<Router.Route name="root" path="/app/" handler={App}>
|
|
||||||
<Router.DefaultRoute handler={SelectOrg} />
|
|
||||||
<Router.NotFoundRoute handler={NotFound} />
|
|
||||||
<Router.Route name="org" path=":org" handler={Org}>
|
|
||||||
<Router.DefaultRoute handler={Dashboard} />
|
|
||||||
<Router.Route name="team" path="teams/:team" handler={Dashboard} />
|
|
||||||
<Router.Route name="user" path="users/:user" handler={Dashboard} />
|
|
||||||
<Router.Route name="repo" path="repos/:repo" handler={Dashboard} />
|
|
||||||
</Router.Route>
|
|
||||||
</Router.Route>
|
|
||||||
];
|
|
||||||
Router.run(routes, Router.HistoryLocation, function(Handler) {
|
|
||||||
React.render(<Handler />, document.body);
|
|
||||||
});
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
var App = React.createClass({
|
||||||
|
mixins: [ReactRouter.Navigation, ReactRouter.State],
|
||||||
|
|
||||||
|
orgsURL: '/api/orgs',
|
||||||
|
teamsURL: '/api/teams',
|
||||||
|
usersURL: '/api/users',
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
orgs: [],
|
||||||
|
org: null,
|
||||||
|
teams: [],
|
||||||
|
team: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.loadOrgs();
|
||||||
|
this.loadTeams();
|
||||||
|
this.loadUsers();
|
||||||
|
},
|
||||||
|
|
||||||
|
loadOrgs: function() {
|
||||||
|
getURL(this.orgsURL, {}, function(res) {
|
||||||
|
this.setState({orgs: res});
|
||||||
|
if (res !== null) {
|
||||||
|
for (var i = 0; i < res.length; i++) {
|
||||||
|
var org = res[i];
|
||||||
|
Storage.set('org', org.login, org);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
loadTeams: function() {
|
||||||
|
getURL(this.teamsURL, {org: this.getParams().org}, function(res) {
|
||||||
|
this.setState({teams: res});
|
||||||
|
if (res !== null) {
|
||||||
|
for (var i = 0; i < res.length; i++) {
|
||||||
|
var team = res[i];
|
||||||
|
Storage.set('team', team.name, team);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
loadUsers: function() {
|
||||||
|
getURL(this.usersURL, {org: this.getParams().org}, function(res) {
|
||||||
|
this.setState({users: res});
|
||||||
|
if (res !== null) {
|
||||||
|
for (var i = 0; i < res.length; i++) {
|
||||||
|
var user = res[i];
|
||||||
|
Storage.set('user', user.login, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="master">
|
||||||
|
<div className="app" id="app">
|
||||||
|
<Menu orgs={this.state.orgs} teams={this.state.teams} />
|
||||||
|
<ReactRouter.RouteHandler />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
var Dashboard = React.createClass({
|
||||||
|
mixins: [ReactRouter.State],
|
||||||
|
|
||||||
|
render: function(){
|
||||||
|
var p = this.getParams(),
|
||||||
|
infoImage, infoImageClass, infoTitle,
|
||||||
|
bcApi, bcItems,
|
||||||
|
sacApi, sacItems;
|
||||||
|
|
||||||
|
if (p.team) {
|
||||||
|
infoTitle = p.team +' Team';
|
||||||
|
infoImageClass = 'team';
|
||||||
|
bcApi = '/api/stat/teams/top';
|
||||||
|
bcItems = ['repo', 'user'],
|
||||||
|
sacApi = '/api/stat/teams/activity';
|
||||||
|
sacItems = ['user', 'repo'];
|
||||||
|
} else if (p.user) {
|
||||||
|
var info = Storage.get('user', p.user);
|
||||||
|
infoImage = info ? info.avatar_url : null;
|
||||||
|
infoTitle = info && info.name ? info.name : p.user;
|
||||||
|
bcApi = '/api/stat/users/top';
|
||||||
|
bcItems = ['repo'],
|
||||||
|
sacApi = '/api/stat/users/activity';
|
||||||
|
sacItems = ['repo'];
|
||||||
|
} else if (p.repo) {
|
||||||
|
infoTitle = p.repo;
|
||||||
|
infoImageClass = 'repo';
|
||||||
|
bcApi = '/api/stat/repos/top';
|
||||||
|
bcItems = ['user', 'team'],
|
||||||
|
sacApi = '/api/stat/repos/activity';
|
||||||
|
sacItems = ['user', 'team'];
|
||||||
|
} else {
|
||||||
|
var info = Storage.get('org', p.org);
|
||||||
|
infoImage = info.avatar_url;
|
||||||
|
infoTitle = info.login;
|
||||||
|
bcApi = '/api/stat/orgs/top';
|
||||||
|
bcItems = ['repo', 'team', 'user'],
|
||||||
|
sacApi = '/api/stat/orgs/activity';
|
||||||
|
sacItems = ['team', 'user', 'repo'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="content">
|
||||||
|
<InfoBlock image={infoImage} className={infoImageClass} title={infoTitle} />
|
||||||
|
<BarChart api={bcApi} params={this.getParams()} items={bcItems} />
|
||||||
|
<StackedAreaChart api={sacApi} params={this.getParams()} items={sacItems} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
var InfoBlock = React.createClass({
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="info-block">
|
||||||
|
<div className={'img'+ (this.props.image ? '' : ' empty') +' '+ this.props.className}
|
||||||
|
style={this.props.image ? {backgroundImage: "url("+ this.props.image +")"} : null} />
|
||||||
|
<h1>{this.props.title}</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,48 @@
|
||||||
|
var Menu = React.createClass({
|
||||||
|
mixins: [ReactRouter.State],
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var renderOrg = function(org) {
|
||||||
|
return (
|
||||||
|
<li key={'org-'+ org.login} className="nav org">
|
||||||
|
<ReactRouter.Link to="org"
|
||||||
|
params={{org: org.login}}
|
||||||
|
query={this.getQuery()}>
|
||||||
|
{org.login}
|
||||||
|
</ReactRouter.Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}.bind(this);
|
||||||
|
var renderTeam = function(team) {
|
||||||
|
return (
|
||||||
|
<li key={'team-'+ team.name} className="nav team">
|
||||||
|
<ReactRouter.Link to="team"
|
||||||
|
params={{org: team.owner, team: team.name}}
|
||||||
|
query={this.getQuery()}>
|
||||||
|
{team.name}
|
||||||
|
</ReactRouter.Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}.bind(this);
|
||||||
|
return (
|
||||||
|
<div className="menu">
|
||||||
|
<ul>
|
||||||
|
<li className="empact">
|
||||||
|
<ReactRouter.Link to="org" params={this.getParams()} className="logo-button">
|
||||||
|
<div className="logo e">e</div>
|
||||||
|
<div className="logo m">m</div>
|
||||||
|
<div className="logo p">p</div>
|
||||||
|
<div className="logo a">a</div>
|
||||||
|
<div className="logo c">c</div>
|
||||||
|
<div className="logo t">t</div>
|
||||||
|
</ReactRouter.Link>
|
||||||
|
</li>
|
||||||
|
<li className="nav header">Organizations:</li>
|
||||||
|
{this.props.orgs.map(renderOrg)}
|
||||||
|
<li className="nav header">Teams:</li>
|
||||||
|
{this.props.teams.map(renderTeam)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,75 @@
|
||||||
|
var WeekIntervalSelector = React.createClass({
|
||||||
|
mixins: [ReactRouter.Navigation, ReactRouter.State],
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
var ms = 1000,
|
||||||
|
daySeconds = 86400,
|
||||||
|
weekSeconds = daySeconds*7,
|
||||||
|
today = new Date(),
|
||||||
|
sunday = new Date(today - daySeconds*ms*today.getDay()),
|
||||||
|
perfectSunday = new Date(Date.UTC(sunday.getFullYear(), sunday.getMonth(), sunday.getDate())),
|
||||||
|
lastWeek = perfectSunday.setHours(0)/ms,
|
||||||
|
firstWeek = lastWeek - 51*weekSeconds;
|
||||||
|
|
||||||
|
var weeks = [];
|
||||||
|
for (var i = lastWeek; i >= firstWeek; i -= weekSeconds) {
|
||||||
|
weeks.push(i);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
weeks: weeks.sort()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChange: function(thing, e) {
|
||||||
|
var params = this.getQuery();
|
||||||
|
params[thing.slice(0, 1)] = e.target.value/100;
|
||||||
|
this.transitionTo(document.location.pathname, null, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var daySeconds = 86400,
|
||||||
|
weekSeconds = daySeconds*7,
|
||||||
|
lastWeek = this.state.weeks[this.state.weeks.length-1],
|
||||||
|
from = (this.getQuery().f ? parseInt(this.getQuery().f, 10)*100 : lastWeek - 29*weekSeconds),
|
||||||
|
to = (this.getQuery().t ? parseInt(this.getQuery().t, 10)*100 : lastWeek);
|
||||||
|
|
||||||
|
var weeksBefore = _(this.state.weeks)
|
||||||
|
.filter(function(week) {
|
||||||
|
return week < to;
|
||||||
|
})
|
||||||
|
.reverse()
|
||||||
|
.value();
|
||||||
|
var weeksAfter = _(this.state.weeks)
|
||||||
|
.filter(function(week) {
|
||||||
|
return week > from;
|
||||||
|
})
|
||||||
|
.reverse()
|
||||||
|
.value();
|
||||||
|
|
||||||
|
var renderOption = function(ts) {
|
||||||
|
return (
|
||||||
|
<option key={ts} value={ts}>{formatDate(ts, true)}</option>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="week-selector">
|
||||||
|
<span>from</span>
|
||||||
|
<div ref="from" className="selector">
|
||||||
|
<em ref="label">{formatDate(from)}</em>
|
||||||
|
<select ref="select" value={from} onChange={this.handleChange.bind(this, 'from')}>
|
||||||
|
{weeksBefore.map(renderOption)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<span>to</span>
|
||||||
|
<div ref="to" className="selector">
|
||||||
|
<em ref="label">{formatDate(to)}</em>
|
||||||
|
<select ref="select" value={to} onChange={this.handleChange.bind(this, 'to')}>
|
||||||
|
{weeksAfter.map(renderOption)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,103 @@
|
||||||
|
var Bar = React.createClass({
|
||||||
|
mixins: [ReactRouter.Navigation, ChartAnimationMixin],
|
||||||
|
|
||||||
|
height: 30,
|
||||||
|
labelPaddingH: 5, // Label horizontal padding
|
||||||
|
labelPaddingV: 2, // Label vertical padding
|
||||||
|
labelMarginV: 5, // Same as padding
|
||||||
|
labelHeight: 16, // Text size
|
||||||
|
labelOuterHeight: 20, // labelHeight + 2*labelPaddingV,
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
labelX: 0,
|
||||||
|
lastLabelX: this.labelPaddingH
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.calculateLabelPosition();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps: function(newProps) {
|
||||||
|
if (_.isEqual(this.props, newProps)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
lastBarX: (this.props.x !== undefined ? this.props.x : newProps.x),
|
||||||
|
lastBarWidth: (this.props.width !== undefined ? this.props.width : newProps.width),
|
||||||
|
lastLabelX: this.state.labelX
|
||||||
|
}, this.calculateLabelPosition);
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateLabelPosition: function() {
|
||||||
|
var val = this.props.value,
|
||||||
|
offset = this.props.offset,
|
||||||
|
label = this.props.item + ': ' + formatNumber(val),
|
||||||
|
labelWidth = textWidth(label),
|
||||||
|
labelOffsetWidth = labelWidth + 2*this.labelPaddingH,
|
||||||
|
labelX;
|
||||||
|
|
||||||
|
if (offset === 0) {
|
||||||
|
labelX = this.labelPaddingH;
|
||||||
|
} else {
|
||||||
|
if (val < 0) {
|
||||||
|
if (offset >= labelOffsetWidth) {
|
||||||
|
labelX = offset - labelOffsetWidth + this.labelPaddingH;
|
||||||
|
} else {
|
||||||
|
labelX = offset + this.labelPaddingH;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (offset + labelOffsetWidth <= this.props.max) {
|
||||||
|
labelX = offset + this.labelPaddingH;
|
||||||
|
} else {
|
||||||
|
labelX = offset - labelOffsetWidth + this.labelPaddingH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
labelX: labelX,
|
||||||
|
barWidth: (this.props.item && this.props.width < 5 ? 5 : this.props.width)
|
||||||
|
}, this.animateAll);
|
||||||
|
},
|
||||||
|
|
||||||
|
animateAll: function() {
|
||||||
|
this.clearAnimations(this.refs.bar);
|
||||||
|
this.clearAnimations(this.refs.label);
|
||||||
|
this.animate(this.refs.bar, 'width', this.state.lastBarWidth, this.state.barWidth);
|
||||||
|
this.animate(this.refs.bar, 'x', this.state.lastBarX, this.props.x);
|
||||||
|
this.animate(this.refs.label, 'x', this.state.lastLabelX, this.state.labelX);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var label = this.props.item ? (this.props.item + ': ' + formatNumber(this.props.value)) : '',
|
||||||
|
labelWidth = textWidth(label),
|
||||||
|
labelOuterWidth = (labelWidth == 0 ? 0 : labelWidth + 2*this.labelPaddingH),
|
||||||
|
barX = (this.state.lastBarX && this.state.lastBarX !== this.props.x
|
||||||
|
? this.state.lastBarX
|
||||||
|
: this.props.x),
|
||||||
|
barWidth = (this.state.lastBarWidth && this.state.lastBarWidth !== this.state.barWidth
|
||||||
|
? this.state.lastBarWidth
|
||||||
|
: this.state.barWidth);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g onClick={this.props.onClick}>
|
||||||
|
<rect ref="bar" className="bar"
|
||||||
|
fill={this.props.color}
|
||||||
|
width={barWidth}
|
||||||
|
height={this.props.height}
|
||||||
|
x={barX}
|
||||||
|
y={this.props.y}
|
||||||
|
rx="2"
|
||||||
|
ry="2" />
|
||||||
|
<text ref="label" className="label"
|
||||||
|
x={this.state.labelX}
|
||||||
|
y={this.props.y + this.labelMarginV + this.labelHeight}>
|
||||||
|
{label}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -193,107 +193,3 @@ var BarChart = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var Bar = React.createClass({
|
|
||||||
mixins: [ReactRouter.Navigation, ChartAnimationMixin],
|
|
||||||
|
|
||||||
height: 30,
|
|
||||||
labelPaddingH: 5, // Label horizontal padding
|
|
||||||
labelPaddingV: 2, // Label vertical padding
|
|
||||||
labelMarginV: 5, // Same as padding
|
|
||||||
labelHeight: 16, // Text size
|
|
||||||
labelOuterHeight: 20, // labelHeight + 2*labelPaddingV,
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
labelX: 0,
|
|
||||||
lastLabelX: this.labelPaddingH
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.calculateLabelPosition();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
|
||||||
if (_.isEqual(this.props, newProps)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
lastBarX: (this.props.x !== undefined ? this.props.x : newProps.x),
|
|
||||||
lastBarWidth: (this.props.width !== undefined ? this.props.width : newProps.width),
|
|
||||||
lastLabelX: this.state.labelX
|
|
||||||
}, this.calculateLabelPosition);
|
|
||||||
},
|
|
||||||
|
|
||||||
calculateLabelPosition: function() {
|
|
||||||
var val = this.props.value,
|
|
||||||
offset = this.props.offset,
|
|
||||||
label = this.props.item + ': ' + formatNumber(val),
|
|
||||||
labelWidth = textWidth(label),
|
|
||||||
labelOffsetWidth = labelWidth + 2*this.labelPaddingH,
|
|
||||||
labelX;
|
|
||||||
|
|
||||||
if (offset === 0) {
|
|
||||||
labelX = this.labelPaddingH;
|
|
||||||
} else {
|
|
||||||
if (val < 0) {
|
|
||||||
if (offset >= labelOffsetWidth) {
|
|
||||||
labelX = offset - labelOffsetWidth + this.labelPaddingH;
|
|
||||||
} else {
|
|
||||||
labelX = offset + this.labelPaddingH;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (offset + labelOffsetWidth <= this.props.max) {
|
|
||||||
labelX = offset + this.labelPaddingH;
|
|
||||||
} else {
|
|
||||||
labelX = offset - labelOffsetWidth + this.labelPaddingH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
labelX: labelX,
|
|
||||||
barWidth: (this.props.item && this.props.width < 5 ? 5 : this.props.width)
|
|
||||||
}, this.animateAll);
|
|
||||||
},
|
|
||||||
|
|
||||||
animateAll: function() {
|
|
||||||
this.clearAnimations(this.refs.bar);
|
|
||||||
this.clearAnimations(this.refs.label);
|
|
||||||
this.animate(this.refs.bar, 'width', this.state.lastBarWidth, this.state.barWidth);
|
|
||||||
this.animate(this.refs.bar, 'x', this.state.lastBarX, this.props.x);
|
|
||||||
this.animate(this.refs.label, 'x', this.state.lastLabelX, this.state.labelX);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var label = this.props.item ? (this.props.item + ': ' + formatNumber(this.props.value)) : '',
|
|
||||||
labelWidth = textWidth(label),
|
|
||||||
labelOuterWidth = (labelWidth == 0 ? 0 : labelWidth + 2*this.labelPaddingH),
|
|
||||||
barX = (this.state.lastBarX && this.state.lastBarX !== this.props.x
|
|
||||||
? this.state.lastBarX
|
|
||||||
: this.props.x),
|
|
||||||
barWidth = (this.state.lastBarWidth && this.state.lastBarWidth !== this.state.barWidth
|
|
||||||
? this.state.lastBarWidth
|
|
||||||
: this.state.barWidth);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g onClick={this.props.onClick}>
|
|
||||||
<rect ref="bar" className="bar"
|
|
||||||
fill={this.props.color}
|
|
||||||
width={barWidth}
|
|
||||||
height={this.props.height}
|
|
||||||
x={barX}
|
|
||||||
y={this.props.y}
|
|
||||||
rx="2"
|
|
||||||
ry="2" />
|
|
||||||
<text ref="label" className="label"
|
|
||||||
x={this.state.labelX}
|
|
||||||
y={this.props.y + this.labelMarginV + this.labelHeight}>
|
|
||||||
{label}
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
var Area = React.createClass({
|
||||||
|
mixins: [ChartAnimationMixin],
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps: function(newProps) {
|
||||||
|
this.setState({
|
||||||
|
lastd: this.props.d || newProps.d,
|
||||||
|
}, this.animateAll);
|
||||||
|
},
|
||||||
|
|
||||||
|
animateAll: function() {
|
||||||
|
this.clearAnimations(this.refs.path);
|
||||||
|
this.animate(this.refs.path, 'd', this.state.lastd, this.props.d);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<path ref="path"
|
||||||
|
className={'path path-'+ this.props.i}
|
||||||
|
d={this.state.lastd || this.props.d}
|
||||||
|
fill={this.props.color}
|
||||||
|
onMouseOver={this.props.onMouseOver}
|
||||||
|
shapeRendering="optimizeQuality" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,68 @@
|
||||||
|
var Axis = React.createClass({
|
||||||
|
topMargin: 2,
|
||||||
|
markHeight: 5,
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
if (this.props.weeks.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var renderMark = function(week, i) {
|
||||||
|
var len = this.props.weeks.length,
|
||||||
|
x = i/(len - 1)*this.props.width,
|
||||||
|
showLabel,
|
||||||
|
ta = (i === 0 // Text anchor for the leftmost label
|
||||||
|
? 'start'
|
||||||
|
: (i === len - 1 // Text anchor for the rightmost label
|
||||||
|
? 'end'
|
||||||
|
: 'middle')); // Text anchor for other labels
|
||||||
|
|
||||||
|
// Thin out labels
|
||||||
|
if (len > 20) {
|
||||||
|
showLabel = (i % 3 === 0);
|
||||||
|
} else if (len > 10) {
|
||||||
|
showLabel = (i % 2 === 0);
|
||||||
|
} else {
|
||||||
|
showLabel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g key={'mark-'+ i}>
|
||||||
|
<line className="axis"
|
||||||
|
x1={x}
|
||||||
|
y1={this.props.y + this.topMargin}
|
||||||
|
x2={x}
|
||||||
|
y2={this.props.y + this.topMargin + this.markHeight} />
|
||||||
|
{!showLabel ? null : <text className="axis-mark"
|
||||||
|
x={x}
|
||||||
|
y={this.props.y + this.topMargin + 14}
|
||||||
|
textAnchor={ta}
|
||||||
|
>
|
||||||
|
{formatDate(week)}
|
||||||
|
</text>}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g ref="axis">
|
||||||
|
<rect // This rect hides area bouncing glitches
|
||||||
|
x="0"
|
||||||
|
y={this.props.y}
|
||||||
|
width={this.props.width}
|
||||||
|
height={this.props.height}
|
||||||
|
fill="#fff" />
|
||||||
|
<line className="axis"
|
||||||
|
x1="0"
|
||||||
|
y1={this.props.y + this.topMargin}
|
||||||
|
x2={this.props.width}
|
||||||
|
y2={this.props.y + this.topMargin} />
|
||||||
|
{this.props.weeks.map(renderMark)}
|
||||||
|
<line className="axis"
|
||||||
|
x1={this.props.width - 1}
|
||||||
|
y1={this.props.y + this.topMargin}
|
||||||
|
x2={this.props.width - 1}
|
||||||
|
y2={this.props.y + this.topMargin + this.markHeight} />
|
||||||
|
</g>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
var Dot = React.createClass({
|
||||||
|
mixins: [ChartAnimationMixin],
|
||||||
|
|
||||||
|
radius: 10,
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps: function(newProps) {
|
||||||
|
this.setState({
|
||||||
|
lastY: this.props.y || newProps.y
|
||||||
|
}, this.animateAll);
|
||||||
|
},
|
||||||
|
|
||||||
|
animateAll: function() {
|
||||||
|
this.clearAnimations(this.refs.dot);
|
||||||
|
this.animate(this.refs.dot, 'cy', this.state.lastY, this.props.y);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<g className={'dot dot-'+ this.props.i} onMouseOver={this.props.onMouseOver}>
|
||||||
|
<circle ref="dot"
|
||||||
|
cx={this.props.x}
|
||||||
|
cy={this.state.lastY || this.props.y}
|
||||||
|
r={this.radius} />
|
||||||
|
<text ref="value"
|
||||||
|
x={this.props.x}
|
||||||
|
y={this.props.y+4}>
|
||||||
|
{this.props.value}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -191,7 +191,7 @@ var StackedAreaChart = React.createClass({
|
||||||
var item = pair[0], path = pair[1];
|
var item = pair[0], path = pair[1];
|
||||||
// NOTE: Rounded bottom corners is a side-effect
|
// NOTE: Rounded bottom corners is a side-effect
|
||||||
return (
|
return (
|
||||||
<StackedArea key={'area-'+ i}
|
<Area key={'area-'+ i}
|
||||||
item={item} i={i}
|
item={item} i={i}
|
||||||
d={roundPathCorners(this.buildPathD(path), 3)}
|
d={roundPathCorners(this.buildPathD(path), 3)}
|
||||||
color={Colors[i]}
|
color={Colors[i]}
|
||||||
|
@ -334,139 +334,3 @@ var StackedAreaChart = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var StackedArea = React.createClass({
|
|
||||||
mixins: [ChartAnimationMixin],
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
|
||||||
this.setState({
|
|
||||||
lastd: this.props.d || newProps.d,
|
|
||||||
}, this.animateAll);
|
|
||||||
},
|
|
||||||
|
|
||||||
animateAll: function() {
|
|
||||||
this.clearAnimations(this.refs.path);
|
|
||||||
this.animate(this.refs.path, 'd', this.state.lastd, this.props.d);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<path ref="path"
|
|
||||||
className={'path path-'+ this.props.i}
|
|
||||||
d={this.state.lastd || this.props.d}
|
|
||||||
fill={this.props.color}
|
|
||||||
onMouseOver={this.props.onMouseOver}
|
|
||||||
shapeRendering="optimizeQuality" />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Dot = React.createClass({
|
|
||||||
mixins: [ChartAnimationMixin],
|
|
||||||
|
|
||||||
radius: 10,
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
|
||||||
this.setState({
|
|
||||||
lastY: this.props.y || newProps.y
|
|
||||||
}, this.animateAll);
|
|
||||||
},
|
|
||||||
|
|
||||||
animateAll: function() {
|
|
||||||
this.clearAnimations(this.refs.dot);
|
|
||||||
this.animate(this.refs.dot, 'cy', this.state.lastY, this.props.y);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<g className={'dot dot-'+ this.props.i} onMouseOver={this.props.onMouseOver}>
|
|
||||||
<circle ref="dot"
|
|
||||||
cx={this.props.x}
|
|
||||||
cy={this.state.lastY || this.props.y}
|
|
||||||
r={this.radius} />
|
|
||||||
<text ref="value"
|
|
||||||
x={this.props.x}
|
|
||||||
y={this.props.y+4}>
|
|
||||||
{this.props.value}
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Axis = React.createClass({
|
|
||||||
topMargin: 2,
|
|
||||||
markHeight: 5,
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (this.props.weeks.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var renderMark = function(week, i) {
|
|
||||||
var len = this.props.weeks.length,
|
|
||||||
x = i/(len - 1)*this.props.width,
|
|
||||||
showLabel,
|
|
||||||
ta = (i === 0 // Text anchor for the leftmost label
|
|
||||||
? 'start'
|
|
||||||
: (i === len - 1 // Text anchor for the rightmost label
|
|
||||||
? 'end'
|
|
||||||
: 'middle')); // Text anchor for other labels
|
|
||||||
|
|
||||||
// Thin out labels
|
|
||||||
if (len > 20) {
|
|
||||||
showLabel = (i % 3 === 0);
|
|
||||||
} else if (len > 10) {
|
|
||||||
showLabel = (i % 2 === 0);
|
|
||||||
} else {
|
|
||||||
showLabel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g key={'mark-'+ i}>
|
|
||||||
<line className="axis"
|
|
||||||
x1={x}
|
|
||||||
y1={this.props.y + this.topMargin}
|
|
||||||
x2={x}
|
|
||||||
y2={this.props.y + this.topMargin + this.markHeight} />
|
|
||||||
{!showLabel ? null : <text className="axis-mark"
|
|
||||||
x={x}
|
|
||||||
y={this.props.y + this.topMargin + 14}
|
|
||||||
textAnchor={ta}
|
|
||||||
>
|
|
||||||
{formatDate(week)}
|
|
||||||
</text>}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g ref="axis">
|
|
||||||
<rect // This rect hides area bouncing glitches
|
|
||||||
x="0"
|
|
||||||
y={this.props.y}
|
|
||||||
width={this.props.width}
|
|
||||||
height={this.props.height}
|
|
||||||
fill="#fff" />
|
|
||||||
<line className="axis"
|
|
||||||
x1="0"
|
|
||||||
y1={this.props.y + this.topMargin}
|
|
||||||
x2={this.props.width}
|
|
||||||
y2={this.props.y + this.topMargin} />
|
|
||||||
{this.props.weeks.map(renderMark)}
|
|
||||||
<line className="axis"
|
|
||||||
x1={this.props.width - 1}
|
|
||||||
y1={this.props.y + this.topMargin}
|
|
||||||
x2={this.props.width - 1}
|
|
||||||
y2={this.props.y + this.topMargin + this.markHeight} />
|
|
||||||
</g>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
var SVGChartMixin = {
|
||||||
|
calculateViewBoxWidth: function() {
|
||||||
|
this.setState({
|
||||||
|
canvasWidth: this.refs.svg.getDOMNode().offsetWidth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
var Org = React.createClass({
|
||||||
|
render: function(){
|
||||||
|
return (
|
||||||
|
<ReactRouter.RouteHandler />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var NotFound = React.createClass({
|
||||||
|
render: function(){
|
||||||
|
return (
|
||||||
|
<div className="content">NOT FOUND :(</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var SelectOrg = React.createClass({
|
||||||
|
render: function(){
|
||||||
|
return (
|
||||||
|
<div className="content">Please select organization from the menu!</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var routes = [
|
||||||
|
<ReactRouter.Route name="root" path="/app/" handler={App}>
|
||||||
|
<ReactRouter.DefaultRoute handler={SelectOrg} />
|
||||||
|
<ReactRouter.NotFoundRoute handler={NotFound} />
|
||||||
|
<ReactRouter.Route name="org" path=":org" handler={Org}>
|
||||||
|
<ReactRouter.DefaultRoute handler={Dashboard} />
|
||||||
|
<ReactRouter.Route name="team" path="teams/:team" handler={Dashboard} />
|
||||||
|
<ReactRouter.Route name="user" path="users/:user" handler={Dashboard} />
|
||||||
|
<ReactRouter.Route name="repo" path="repos/:repo" handler={Dashboard} />
|
||||||
|
</ReactRouter.Route>
|
||||||
|
</ReactRouter.Route>
|
||||||
|
];
|
||||||
|
|
||||||
|
ReactRouter.run(routes, ReactRouter.HistoryLocation, function(Handler) {
|
||||||
|
React.render(<Handler />, document.body);
|
||||||
|
});
|
Loading…
Reference in New Issue