From e2ad2739cdf1e66d76a22f15c698a5cad0239097 Mon Sep 17 00:00:00 2001 From: Gregory Eremin Date: Wed, 18 Mar 2015 17:14:22 +0700 Subject: [PATCH] Split JS code by classes into different files --- app/index.html | 23 +- app/js/storage.js | 10 + .../charts.jsx => js/svg_text_width.js} | 12 +- app/jsx/app.jsx | 311 ------------------ app/jsx/app/app.jsx | 69 ++++ app/jsx/app/dashboard.jsx | 50 +++ app/jsx/app/info_block.jsx | 11 + app/jsx/app/menu.jsx | 48 +++ app/jsx/app/week_interval_selector.jsx | 75 +++++ app/jsx/charts/bc/bar.jsx | 103 ++++++ app/jsx/charts/{ => bc}/bar_chart.jsx | 104 ------ ...nimation.jsx => chart_animation_mixin.jsx} | 0 .../charts/{data.jsx => chart_data_mixin.jsx} | 0 app/jsx/charts/sac/area.jsx | 29 ++ app/jsx/charts/sac/axis.jsx | 68 ++++ app/jsx/charts/sac/dot.jsx | 36 ++ .../charts/{ => sac}/stacked_area_chart.jsx | 138 +------- app/jsx/charts/svg_chart_mixin.jsx | 7 + app/jsx/start.jsx | 40 +++ 19 files changed, 566 insertions(+), 568 deletions(-) create mode 100644 app/js/storage.js rename app/{jsx/charts/charts.jsx => js/svg_text_width.js} (76%) delete mode 100644 app/jsx/app.jsx create mode 100644 app/jsx/app/app.jsx create mode 100644 app/jsx/app/dashboard.jsx create mode 100644 app/jsx/app/info_block.jsx create mode 100644 app/jsx/app/menu.jsx create mode 100644 app/jsx/app/week_interval_selector.jsx create mode 100644 app/jsx/charts/bc/bar.jsx rename app/jsx/charts/{ => bc}/bar_chart.jsx (64%) rename app/jsx/charts/{animation.jsx => chart_animation_mixin.jsx} (100%) rename app/jsx/charts/{data.jsx => chart_data_mixin.jsx} (100%) create mode 100644 app/jsx/charts/sac/area.jsx create mode 100644 app/jsx/charts/sac/axis.jsx create mode 100644 app/jsx/charts/sac/dot.jsx rename app/jsx/charts/{ => sac}/stacked_area_chart.jsx (73%) create mode 100644 app/jsx/charts/svg_chart_mixin.jsx create mode 100644 app/jsx/start.jsx diff --git a/app/index.html b/app/index.html index 168b0ec..4b6416b 100644 --- a/app/index.html +++ b/app/index.html @@ -22,12 +22,23 @@ + + - - - + + + - - - + + + + + + + + + + + + diff --git a/app/js/storage.js b/app/js/storage.js new file mode 100644 index 0000000..5e3f407 --- /dev/null +++ b/app/js/storage.js @@ -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); + } +}; diff --git a/app/jsx/charts/charts.jsx b/app/js/svg_text_width.js similarity index 76% rename from app/jsx/charts/charts.jsx rename to app/js/svg_text_width.js index 5381533..b089505 100644 --- a/app/jsx/charts/charts.jsx +++ b/app/js/svg_text_width.js @@ -1,17 +1,9 @@ -var SVGChartMixin = { - calculateViewBoxWidth: function() { - this.setState({ - canvasWidth: this.refs.svg.getDOMNode().offsetWidth - }); - } -}; - var fontFamily = "'Open Sans', Helvetica, Arial, sans-serif", fontSize = 16; function textWidth(str) { - var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); - text = document.createElementNS('http://www.w3.org/2000/svg', "text"); + var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.width = 500; svg.height = 500; diff --git a/app/jsx/app.jsx b/app/jsx/app.jsx deleted file mode 100644 index 789c5d8..0000000 --- a/app/jsx/app.jsx +++ /dev/null @@ -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 ( -
-
- - -
-
- ); - } -}); - -var Menu = React.createClass({ - mixins: [Router.State], - - render: function() { - var renderOrg = function(org) { - return ( -
  • - - {org.login} - -
  • - ) - }.bind(this); - var renderTeam = function(team) { - return ( -
  • - - {team.name} - -
  • - ) - }.bind(this); - return ( -
    - -
    - ); - } -}); - -var Org = React.createClass({ - render: function(){ - return ( - - ); - } -}); - -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 ( -
    - - - -
    - ); - } -}); - -var NotFound = React.createClass({ - render: function(){ - return ( -
    NOT FOUND :(
    - ); - } -}); - -var SelectOrg = React.createClass({ - render: function(){ - return ( -
    Please select organization from the menu!
    - ); - } -}); - -var InfoBlock = React.createClass({ - render: function() { - return ( -
    -
    -

    {this.props.title}

    -
    - ) - } -}); - -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 ( - - ); - }; - - return ( -
    - from -
    - {formatDate(from)} - -
    - to -
    - {formatDate(to)} - -
    -
    - ); - } -}); - -var routes = [ - - - - - - - - - - -]; -Router.run(routes, Router.HistoryLocation, function(Handler) { - React.render(, document.body); -}); diff --git a/app/jsx/app/app.jsx b/app/jsx/app/app.jsx new file mode 100644 index 0000000..6d37edb --- /dev/null +++ b/app/jsx/app/app.jsx @@ -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 ( +
    +
    + + +
    +
    + ); + } +}); diff --git a/app/jsx/app/dashboard.jsx b/app/jsx/app/dashboard.jsx new file mode 100644 index 0000000..de24344 --- /dev/null +++ b/app/jsx/app/dashboard.jsx @@ -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 ( +
    + + + +
    + ); + } +}); diff --git a/app/jsx/app/info_block.jsx b/app/jsx/app/info_block.jsx new file mode 100644 index 0000000..fd69df4 --- /dev/null +++ b/app/jsx/app/info_block.jsx @@ -0,0 +1,11 @@ +var InfoBlock = React.createClass({ + render: function() { + return ( +
    +
    +

    {this.props.title}

    +
    + ) + } +}); diff --git a/app/jsx/app/menu.jsx b/app/jsx/app/menu.jsx new file mode 100644 index 0000000..3733551 --- /dev/null +++ b/app/jsx/app/menu.jsx @@ -0,0 +1,48 @@ +var Menu = React.createClass({ + mixins: [ReactRouter.State], + + render: function() { + var renderOrg = function(org) { + return ( +
  • + + {org.login} + +
  • + ) + }.bind(this); + var renderTeam = function(team) { + return ( +
  • + + {team.name} + +
  • + ) + }.bind(this); + return ( +
    +
      +
    • + +
      e
      +
      m
      +
      p
      +
      a
      +
      c
      +
      t
      +
      +
    • +
    • Organizations:
    • + {this.props.orgs.map(renderOrg)} +
    • Teams:
    • + {this.props.teams.map(renderTeam)} +
    +
    + ); + } +}); diff --git a/app/jsx/app/week_interval_selector.jsx b/app/jsx/app/week_interval_selector.jsx new file mode 100644 index 0000000..aae5d63 --- /dev/null +++ b/app/jsx/app/week_interval_selector.jsx @@ -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 ( + + ); + }; + + return ( +
    + from +
    + {formatDate(from)} + +
    + to +
    + {formatDate(to)} + +
    +
    + ); + } +}); diff --git a/app/jsx/charts/bc/bar.jsx b/app/jsx/charts/bc/bar.jsx new file mode 100644 index 0000000..c241ddd --- /dev/null +++ b/app/jsx/charts/bc/bar.jsx @@ -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 ( + + + + {label} + + + ); + } +}); diff --git a/app/jsx/charts/bar_chart.jsx b/app/jsx/charts/bc/bar_chart.jsx similarity index 64% rename from app/jsx/charts/bar_chart.jsx rename to app/jsx/charts/bc/bar_chart.jsx index c8440b4..387541a 100644 --- a/app/jsx/charts/bar_chart.jsx +++ b/app/jsx/charts/bc/bar_chart.jsx @@ -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 ( - - - - {label} - - - ); - } -}); diff --git a/app/jsx/charts/animation.jsx b/app/jsx/charts/chart_animation_mixin.jsx similarity index 100% rename from app/jsx/charts/animation.jsx rename to app/jsx/charts/chart_animation_mixin.jsx diff --git a/app/jsx/charts/data.jsx b/app/jsx/charts/chart_data_mixin.jsx similarity index 100% rename from app/jsx/charts/data.jsx rename to app/jsx/charts/chart_data_mixin.jsx diff --git a/app/jsx/charts/sac/area.jsx b/app/jsx/charts/sac/area.jsx new file mode 100644 index 0000000..0f12d4d --- /dev/null +++ b/app/jsx/charts/sac/area.jsx @@ -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 ( + + ); + } +}); diff --git a/app/jsx/charts/sac/axis.jsx b/app/jsx/charts/sac/axis.jsx new file mode 100644 index 0000000..fdbfbf2 --- /dev/null +++ b/app/jsx/charts/sac/axis.jsx @@ -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 ( + + + {!showLabel ? null : + {formatDate(week)} + } + + ); + }.bind(this); + + return ( + + + + {this.props.weeks.map(renderMark)} + + + ) + } +}); diff --git a/app/jsx/charts/sac/dot.jsx b/app/jsx/charts/sac/dot.jsx new file mode 100644 index 0000000..7f3a9d4 --- /dev/null +++ b/app/jsx/charts/sac/dot.jsx @@ -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 ( + + + + {this.props.value} + + + ); + } +}); diff --git a/app/jsx/charts/stacked_area_chart.jsx b/app/jsx/charts/sac/stacked_area_chart.jsx similarity index 73% rename from app/jsx/charts/stacked_area_chart.jsx rename to app/jsx/charts/sac/stacked_area_chart.jsx index c5d4873..0386d05 100644 --- a/app/jsx/charts/stacked_area_chart.jsx +++ b/app/jsx/charts/sac/stacked_area_chart.jsx @@ -191,7 +191,7 @@ var StackedAreaChart = React.createClass({ var item = pair[0], path = pair[1]; // NOTE: Rounded bottom corners is a side-effect return ( - - ); - } -}); - -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 ( - - - - {this.props.value} - - - ); - } -}); - -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 ( - - - {!showLabel ? null : - {formatDate(week)} - } - - ); - }.bind(this); - - return ( - - - - {this.props.weeks.map(renderMark)} - - - ) - } -}); diff --git a/app/jsx/charts/svg_chart_mixin.jsx b/app/jsx/charts/svg_chart_mixin.jsx new file mode 100644 index 0000000..30b24f4 --- /dev/null +++ b/app/jsx/charts/svg_chart_mixin.jsx @@ -0,0 +1,7 @@ +var SVGChartMixin = { + calculateViewBoxWidth: function() { + this.setState({ + canvasWidth: this.refs.svg.getDOMNode().offsetWidth + }); + } +}; diff --git a/app/jsx/start.jsx b/app/jsx/start.jsx new file mode 100644 index 0000000..19ac319 --- /dev/null +++ b/app/jsx/start.jsx @@ -0,0 +1,40 @@ +var Org = React.createClass({ + render: function(){ + return ( + + ); + } +}); + +var NotFound = React.createClass({ + render: function(){ + return ( +
    NOT FOUND :(
    + ); + } +}); + +var SelectOrg = React.createClass({ + render: function(){ + return ( +
    Please select organization from the menu!
    + ); + } +}); + +var routes = [ + + + + + + + + + + +]; + +ReactRouter.run(routes, ReactRouter.HistoryLocation, function(Handler) { + React.render(, document.body); +});