es6 classes for React; closes #112
This commit is contained in:
parent
b57bc76edd
commit
c3896a3598
6
.babelrc
6
.babelrc
|
@ -1,3 +1,7 @@
|
|||
{
|
||||
"presets": [ "react", "es2015" ]
|
||||
"presets": [
|
||||
"react",
|
||||
"es2015",
|
||||
"stage-0"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "burnchart",
|
||||
"version": "3.2.1",
|
||||
"version": "3.2.2",
|
||||
"description": "GitHub Burndown Chart as a Service",
|
||||
"author": "Radek Stepan <dev@radekstepan.com> (http://radekstepan.com)",
|
||||
"license": "AGPL-3.0",
|
||||
|
@ -21,6 +21,7 @@
|
|||
"babel": "^6.3.26",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"babel-preset-stage-0": "^6.5.0",
|
||||
"babel-register": "^6.4.3",
|
||||
"babelify": "^7.2.0",
|
||||
"browserify": "^13.0.0",
|
||||
|
|
|
@ -1060,5 +1060,5 @@ ul li {
|
|||
color: #C1041C;
|
||||
}
|
||||
#app.theme--monza #page #content #projects table tr td.action {
|
||||
background: #760211;
|
||||
color: #760211;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -27,8 +27,8 @@ export default {
|
|||
},
|
||||
// Request pertaining.
|
||||
"request": {
|
||||
// Default timeout of 5s.
|
||||
"timeout": 5e3
|
||||
// Default timeout of 10s.
|
||||
"timeout": 1e4
|
||||
},
|
||||
// The app theme; 'monza' is the default red theme.
|
||||
"theme": "monza"
|
||||
|
|
|
@ -8,33 +8,42 @@ import actions from '../actions/appActions.js';
|
|||
import Icon from './Icon.jsx';
|
||||
import S from './Space.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class AddProjectForm extends React.Component {
|
||||
|
||||
displayName: 'AddProjectForm.jsx',
|
||||
displayName: 'AddProjectForm.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Blank input.
|
||||
this.state = { 'val': '' };
|
||||
// Bindings.
|
||||
this._onChange = this._onChange.bind(this);
|
||||
this._onAdd = this._onAdd.bind(this);
|
||||
}
|
||||
|
||||
// Sign user in.
|
||||
_onSignIn() {
|
||||
actions.emit('user.signin');
|
||||
},
|
||||
}
|
||||
|
||||
_onChange(evt, { newValue }) {
|
||||
this.setState({ 'val': newValue });
|
||||
},
|
||||
}
|
||||
|
||||
// Get a list of repo suggestions.
|
||||
_onGetList({ value }) {
|
||||
actions.emit('projects.search', value);
|
||||
},
|
||||
}
|
||||
|
||||
// What should be the value of the suggestion.
|
||||
_getListValue(value) {
|
||||
return value;
|
||||
},
|
||||
}
|
||||
|
||||
// How do we render the repo?
|
||||
_renderListValue(value) {
|
||||
return value;
|
||||
},
|
||||
}
|
||||
|
||||
// Add the project.
|
||||
_onAdd() {
|
||||
|
@ -46,12 +55,7 @@ export default React.createClass({
|
|||
actions.emit('projects.add', { owner, name });
|
||||
// Redirect to the dashboard.
|
||||
App.navigate({ 'to': 'projects' });
|
||||
},
|
||||
|
||||
// Blank input.
|
||||
getInitialState() {
|
||||
return { 'val': '' };
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
let user;
|
||||
|
@ -106,11 +110,6 @@ export default React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
// Focus input field on mount.
|
||||
componentDidMount() {
|
||||
if ('el' in this.refs) this.refs.el.focus();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,13 +7,17 @@ d3Tip(d3);
|
|||
import lines from '../modules/chart/lines.js';
|
||||
import axes from '../modules/chart/axes.js';
|
||||
|
||||
export default React.createClass({
|
||||
export default class Chart extends React.Component {
|
||||
|
||||
displayName: 'Chart.jsx',
|
||||
displayName: 'Chart.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div id="chart" ref="el" style={this.props.style} />;
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let { data } = this.props;
|
||||
|
@ -154,4 +158,4 @@ export default React.createClass({
|
|||
.on('mouseout', tooltip.hide);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,13 +7,17 @@ import actions from '../actions/appActions.js';
|
|||
import Icon from './Icon.jsx';
|
||||
import Link from './Link.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class EditProjects extends React.Component {
|
||||
|
||||
displayName: 'EditProjects.jsx',
|
||||
displayName: 'EditProjects.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
_onDelete(project) {
|
||||
actions.emit('projects.delete', project);
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
let { projects } = this.props;
|
||||
|
@ -55,4 +59,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import React from 'react';
|
||||
|
||||
export default React.createClass({
|
||||
export default class Footer extends React.Component {
|
||||
|
||||
displayName: 'Footer.jsx',
|
||||
displayName: 'Footer.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -14,4 +18,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,24 +6,28 @@ import Notify from './Notify.jsx';
|
|||
import Icon from './Icon.jsx';
|
||||
import Link from './Link.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class Header extends React.Component {
|
||||
|
||||
displayName: 'Header.jsx',
|
||||
displayName: 'Header.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
// Sign user in.
|
||||
_onSignIn() {
|
||||
actions.emit('user.signin');
|
||||
},
|
||||
}
|
||||
|
||||
// Sign user out.
|
||||
_onSignOut() {
|
||||
actions.emit('user.signout');
|
||||
},
|
||||
}
|
||||
|
||||
// Add example projects.
|
||||
_onDemo() {
|
||||
actions.emit('projects.demo');
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
// From app store.
|
||||
|
@ -79,4 +83,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,14 +5,18 @@ import actions from '../actions/appActions.js';
|
|||
import Icon from './Icon.jsx';
|
||||
import Link from './Link.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class Hero extends React.Component {
|
||||
|
||||
displayName: 'Hero.jsx',
|
||||
displayName: 'Hero.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
// Add example projects.
|
||||
_onDemo() {
|
||||
actions.emit('projects.demo');
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -35,4 +39,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import format from '../modules/format.js';
|
||||
|
||||
// Fontello icon hex codes.
|
||||
let codes = {
|
||||
const codes = {
|
||||
'delete': '\e800', // Font Awesome - trash-empty
|
||||
'settings': '\e801', // Font Awesome - cog
|
||||
'pencil': '\e802', // Font Awesome - pencil
|
||||
|
@ -23,15 +23,19 @@ let codes = {
|
|||
'fire': '\e810' // Maki - fire-station
|
||||
};
|
||||
|
||||
export default React.createClass({
|
||||
export default class Icon extends React.Component {
|
||||
|
||||
displayName: 'Icon.jsx',
|
||||
displayName: 'Icon.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let name = this.props.name;
|
||||
const name = this.props.name;
|
||||
|
||||
if (name && name in codes) {
|
||||
let code = format.hexToDec(codes[name]);
|
||||
const code = format.hexToDec(codes[name]);
|
||||
return (
|
||||
<span
|
||||
className={`icon ${name}`}
|
||||
|
@ -43,4 +47,4 @@ export default React.createClass({
|
|||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,15 +2,19 @@ import React from 'react';
|
|||
|
||||
import App from '../App.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class Link extends React.Component {
|
||||
|
||||
displayName: 'Link.jsx',
|
||||
displayName: 'Link.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
// Navigate to a route.
|
||||
_navigate(link, evt) {
|
||||
App.navigate(link);
|
||||
evt.preventDefault();
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
let route = this.props.route;
|
||||
|
@ -27,4 +31,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,14 +9,18 @@ import actions from '../actions/appActions.js';
|
|||
import Icon from './Icon.jsx';
|
||||
import Link from './Link.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class Milestones extends React.Component {
|
||||
|
||||
displayName: 'Milestones.jsx',
|
||||
displayName: 'Milestones.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
// Cycle through milestones sort order.
|
||||
_onSort() {
|
||||
actions.emit('projects.sort');
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
let { projects, project } = this.props;
|
||||
|
@ -119,4 +123,4 @@ export default React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,17 +5,14 @@ import actions from '../actions/appActions.js';
|
|||
|
||||
import Icon from './Icon.jsx';
|
||||
|
||||
let Notify = React.createClass({
|
||||
class Notify extends React.Component {
|
||||
|
||||
displayName: 'Notify.jsx',
|
||||
displayName: 'Notify.jsx'
|
||||
|
||||
// Close notification.
|
||||
_onClose() {
|
||||
actions.emit('system.notify');
|
||||
},
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
this.props = {
|
||||
// No text.
|
||||
'text': null,
|
||||
// Grey style.
|
||||
|
@ -25,7 +22,12 @@ let Notify = React.createClass({
|
|||
// Just announcing.
|
||||
'icon': 'megaphone'
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
// Close notification.
|
||||
_onClose() {
|
||||
actions.emit('system.notify');
|
||||
}
|
||||
|
||||
render() {
|
||||
let { text, system, type, icon, ttl } = this.props;
|
||||
|
@ -51,9 +53,9 @@ let Notify = React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
export default React.createClass({
|
||||
export default class NotifyWrapper extends React.Component {
|
||||
|
||||
// TODO: animate in
|
||||
render() {
|
||||
|
@ -73,4 +75,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import React from 'react';
|
||||
|
||||
// Inserts a space before rendering text.
|
||||
export default React.createClass({
|
||||
export default class Space extends React.Component {
|
||||
|
||||
displayName: 'Space.jsx',
|
||||
displayName: 'Space.jsx'
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <span> </span>;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,18 +2,20 @@ import React from 'react';
|
|||
|
||||
import actions from '../../actions/appActions.js';
|
||||
|
||||
import Page from '../../lib/PageMixin.js';
|
||||
import Page from '../../lib/PageClass.js';
|
||||
|
||||
import Notify from '../Notify.jsx';
|
||||
import Header from '../Header.jsx';
|
||||
import Footer from '../Footer.jsx';
|
||||
import AddProjectForm from '../AddProjectForm.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class AddProjectPage extends Page {
|
||||
|
||||
displayName: 'AddProjectPage.jsx',
|
||||
displayName: 'AddProjectPage.jsx'
|
||||
|
||||
mixins: [ Page ],
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -35,4 +37,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import Page from '../../lib/PageMixin.js';
|
||||
import Page from '../../lib/PageClass.js';
|
||||
|
||||
import format from '../../modules/format.js';
|
||||
|
||||
|
@ -10,11 +10,13 @@ import Header from '../Header.jsx';
|
|||
import Footer from '../Footer.jsx';
|
||||
import Chart from '../Chart.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class ChartPage extends Page {
|
||||
|
||||
displayName: 'ChartPage.jsx',
|
||||
displayName: 'ChartPage.jsx'
|
||||
|
||||
mixins: [ Page ],
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let content;
|
||||
|
@ -70,4 +72,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import Page from '../../lib/PageMixin.js';
|
||||
import Page from '../../lib/PageClass.js';
|
||||
|
||||
import Notify from '../Notify.jsx';
|
||||
import Header from '../Header.jsx';
|
||||
|
@ -9,17 +9,18 @@ import Footer from '../Footer.jsx';
|
|||
import Milestones from '../Milestones.jsx';
|
||||
import Chart from '../Chart.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class MilestonePage extends Page {
|
||||
|
||||
displayName: 'MilestonesPage.jsx',
|
||||
displayName: 'MilestonesPage.jsx'
|
||||
|
||||
mixins: [ Page ],
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let content;
|
||||
if (!this.state.app.system.loading) {
|
||||
let projects = this.state.projects;
|
||||
|
||||
// Create the all milestones payload.
|
||||
let data;
|
||||
_.find(projects.list, (obj) => {
|
||||
|
@ -83,4 +84,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import React from 'react';
|
||||
|
||||
import Page from '../../lib/PageMixin.js';
|
||||
import Page from '../../lib/PageClass.js';
|
||||
|
||||
// TODO: implement
|
||||
export default React.createClass({
|
||||
export default class NotFoundPage extends Page {
|
||||
|
||||
displayName: 'NotFoundPage.jsx',
|
||||
displayName: 'NotFoundPage.jsx'
|
||||
|
||||
mixins: [ Page ],
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>Page {this.props.path} not found</div>;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import Page from '../../lib/PageMixin.js';
|
||||
import Page from '../../lib/PageClass.js';
|
||||
|
||||
import Notify from '../Notify.jsx';
|
||||
import Header from '../Header.jsx';
|
||||
|
@ -9,23 +10,23 @@ import Milestones from '../Milestones.jsx';
|
|||
import EditProjects from '../EditProjects.jsx';
|
||||
import Hero from '../Hero.jsx';
|
||||
|
||||
export default React.createClass({
|
||||
export default class ProjectsPage extends Page {
|
||||
|
||||
displayName: 'ProjectsPage.jsx',
|
||||
displayName: 'ProjectsPage.jsx'
|
||||
|
||||
mixins: [ Page ],
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Start the page in a view mode.
|
||||
// NOTE probably move into its own component so we don't merge state.
|
||||
_.merge(this.state, { 'edit': false });
|
||||
// Bindings.
|
||||
this._onToggleMode = this._onToggleMode.bind(this);
|
||||
}
|
||||
|
||||
// Toggle between edit and view mode.
|
||||
_onToggleMode() {
|
||||
this.setState({ 'edit': !this.state.edit });
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
// Start the page in a view mode.
|
||||
'edit': false
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
let content;
|
||||
|
@ -66,4 +67,4 @@ export default React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@ export default class EventEmitter {
|
|||
// Add a listener on this path/regex.
|
||||
on(path, cb) {
|
||||
if (!_.isRegExp(path)) path = new RegExp(`^${path}$`);
|
||||
this.list.push({ pattern: path, cb: cb });
|
||||
this.list.push({ 'pattern': path, cb: cb });
|
||||
}
|
||||
|
||||
// Add a listener to all events.
|
||||
onAny(cb) {
|
||||
this.list.push({ pattern: /./, cb: cb });
|
||||
this.list.push({ 'pattern': /./, cb: cb });
|
||||
}
|
||||
|
||||
// Assume we can have multiple.
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import stores from '../stores';
|
||||
|
||||
export default class Page extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// State contains our store data.
|
||||
// NOTE top-level page components shouldn't modify the state.
|
||||
this.state = this._getData();
|
||||
// Bindings.
|
||||
this._onChange = this._onChange.bind(this);
|
||||
}
|
||||
|
||||
// Get the POJO of the store.
|
||||
_getData(store) {
|
||||
let obj = {};
|
||||
if (store) {
|
||||
obj[store] = stores[store].get();
|
||||
} else {
|
||||
// Get all stores.
|
||||
for (let key in stores) {
|
||||
obj[key] = stores[key].get();
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Update the state when store changes.
|
||||
_onChange(store, val, key) {
|
||||
if (!this._isMounted) return;
|
||||
this.setState(this._getData(store));
|
||||
}
|
||||
|
||||
// Listen to all events (data changes).
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
|
||||
for (let key in stores) {
|
||||
stores[key].onAny(_.partial(this._onChange, key));
|
||||
}
|
||||
}
|
||||
|
||||
// Stop listening to store changes.
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
|
||||
for (let key in stores) {
|
||||
stores[key].clean(this._onChange);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
import stores from '../stores';
|
||||
|
||||
export default {
|
||||
|
||||
// Get the POJO of the store.
|
||||
_getData(store) {
|
||||
let obj = {};
|
||||
if (store) {
|
||||
obj[store] = stores[store].get();
|
||||
} else {
|
||||
// Get all stores.
|
||||
let key;
|
||||
for (key in stores) {
|
||||
obj[key] = stores[key].get();
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
_onChange(store, val, key) {
|
||||
if (this.isMounted()) { // not ideal
|
||||
this.setState(this._getData(store));
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return this._getData();
|
||||
},
|
||||
|
||||
// Listen to all events (data changes).
|
||||
componentDidMount() {
|
||||
let key;
|
||||
for (key in stores) {
|
||||
stores[key].onAny(_.partial(this._onChange, key));
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
let key;
|
||||
for (key in stores) {
|
||||
stores[key].clean(this._onChange);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
|
@ -73,7 +73,7 @@
|
|||
}
|
||||
|
||||
&.action {
|
||||
background: darken(@strong_color, 15%);
|
||||
color: darken(@strong_color, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue