diff --git a/.babelrc b/.babelrc index 005e78a..fd23462 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,5 @@ { + "plugins": ["transform-class-properties"], "presets": [ "stage-0", "es2015", diff --git a/.eslintrc b/.eslintrc index 0cf4c3d..75f7a6f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -34,9 +34,8 @@ "react/react-in-jsx-scope": 2, "react/no-unknown-property": [2], "react/prop-types": 2, - "react/require-extension": [2, { "extensions": [".js", ".jsx"] }], "react/self-closing-comp": [2], - "react/wrap-multilines": [2], + "react/jsx-wrap-multilines": [2], "semi": [2, "always"], "keyword-spacing": [2, { "before": true, "after": true }], "space-before-blocks": 2, diff --git a/package.json b/package.json index cd32a30..af3f6fa 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "babel-cli": "^6.4.0", "babel-eslint": "^7.2.1", + "babel-plugin-transform-class-properties": "^6.24.1", "babel-polyfill": "^6.3.14", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", @@ -44,9 +45,9 @@ "isomorphic-fetch": "^2.2.0", "lodash.debounce": "^4.0.3", "minifyify": "^7.1.0", + "prop-types": "^15.5.9", "react": "^15.0.2", "react-dom": "^15.0.2", - "react-pure-render": "^1.0.2", "remark": "^7.0.0", "remark-html": "^6.0.0", "remark-slug": "^4.1.0", diff --git a/src/components/app.js b/src/components/app.js index c6421a8..00ec793 100644 --- a/src/components/app.js +++ b/src/components/app.js @@ -1,8 +1,8 @@ import React from 'react'; import Navigation from './navigation'; +import PropTypes from 'prop-types'; import Content from './content'; import RoundedToggle from './rounded_toggle'; -import PureRenderMixin from 'react-pure-render/mixin'; import GithubSlugger from 'github-slugger'; import debounce from 'lodash.debounce'; import { brandNames, brandClasses } from '../custom'; @@ -41,13 +41,13 @@ let debouncedReplaceState = debounce(hash => { window.history.replaceState('', '', hash); }, 100); -var App = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - content: React.PropTypes.string.isRequired, - ast: React.PropTypes.object.isRequired - }, - getInitialState() { +export default class App extends React.PureComponent { + static propTypes = { + content: PropTypes.string.isRequired, + ast: PropTypes.object.isRequired + } + constructor(props) { + super(props); var active = 'Introduction'; if (process.browser) { @@ -70,7 +70,7 @@ var App = React.createClass({ active = headingForHash.children[0].value; } } - return { + this.state = { // media queryMatches mqls: mqls, // object of currently matched queries, like { desktop: true } @@ -82,7 +82,7 @@ var App = React.createClass({ showNav: false }; } else { - return { + this.state = { mqls: { }, queryMatches: { desktop: true @@ -92,17 +92,17 @@ var App = React.createClass({ showNav: false }; } - }, + } toggleNav() { this.setState({ showNav: !this.state.showNav }); - }, + } componentDidMount() { this.mediaQueryChanged(); this.onScroll = debounce(this.onScrollImmediate, 100); document.addEventListener('scroll', this.onScroll); this.onScrollImmediate(); - }, - onScrollImmediate() { + } + onScrollImmediate = () => { var sections = document.querySelectorAll('div.section'); if (!sections.length) return; for (var i = 0; i < sections.length; i++) { @@ -114,7 +114,7 @@ var App = React.createClass({ return; } } - }, + } mediaQueryChanged() { this.setState({ queryMatches: this.state.mqls.reduce((memo, q) => { @@ -122,19 +122,19 @@ var App = React.createClass({ return memo; }, {}) }); - }, + } componentWillUnmount() { this.state.mqls.forEach(q => q.removeListener(this.mediaQueryChanged)); document.body.removeEventListener('scroll', this.onScroll); - }, - onChangeLanguage(language) { + } + onChangeLanguage = (language) => { this.setState({ language }, () => { if (window.history) { window.history.pushState(null, null, `?${qs.stringify({ language: language.title })}${window.location.hash}`); } }); - }, + } componentDidUpdate(_, prevState) { if (prevState.activeSection !== this.state.activeSection) { // when the section changes, replace the hash @@ -144,7 +144,7 @@ var App = React.createClass({ // when the language changes, use the hash to set scroll window.location.hash = window.location.hash; } - }, + } navigationItemClicked(activeSection) { setTimeout(() => { this.setState({ activeSection }); @@ -152,12 +152,12 @@ var App = React.createClass({ if (!this.state.queryMatches.desktop) { this.toggleNav(); } - }, + } toggleColumnMode() { this.setState({ columnMode: this.state.columnMode === 1 ? 2 : 1 }); - }, + } render() { let ast = JSON.parse(JSON.stringify(this.props.ast)); let { activeSection, queryMatches, showNav, columnMode } = this.state; @@ -237,6 +237,4 @@ var App = React.createClass({ ); } -}); - -module.exports = App; +} diff --git a/src/components/content.js b/src/components/content.js index 93d6178..6c6195c 100644 --- a/src/components/content.js +++ b/src/components/content.js @@ -1,14 +1,14 @@ import React from 'react'; +import PropTypes from 'prop-types'; import Section from './section'; -import PureRenderMixin from 'react-pure-render/mixin'; import GithubSlugger from 'github-slugger'; import { transformURL } from '../custom'; let slugger = new GithubSlugger(); let slug = title => { slugger.reset(); return slugger.slug(title); }; -var roundedToggleOptionType = React.PropTypes.shape({ - title: React.PropTypes.string, - value: React.PropTypes.string +var roundedToggleOptionType = PropTypes.shape({ + title: PropTypes.string, + value: PropTypes.string }); function chunkifyAST(ast, language) { @@ -71,24 +71,21 @@ function chunkifyAST(ast, language) { }); } -var Content = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - ast: React.PropTypes.object.isRequired, +export default class Content extends React.PureComponent { + static propTypes = { + ast: PropTypes.object.isRequired, language: roundedToggleOptionType, - leftClassname: React.PropTypes.string.isRequired, - rightClassname: React.PropTypes.string.isRequired - }, + leftClassname: PropTypes.string.isRequired, + rightClassname: PropTypes.string.isRequired + } render() { let { ast, language, leftClassname, rightClassname } = this.props; return (
- {chunkifyAST(ast, language.value).map((chunk, i) =>
(
)} + key={i} />))}
); } -}); - -module.exports = Content; +} diff --git a/src/components/navigation.js b/src/components/navigation.js index 4c667eb..862ec26 100644 --- a/src/components/navigation.js +++ b/src/components/navigation.js @@ -1,5 +1,5 @@ import React from 'react'; -import PureRenderMixin from 'react-pure-render/mixin'; +import PropTypes from 'prop-types'; import NavigationItem from './navigation_item'; import { footerContent } from '../custom'; @@ -23,13 +23,12 @@ function getAllInSection(headings, idx) { return activeHeadings; } -var Navigation = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - ast: React.PropTypes.object.isRequired, - activeSection: React.PropTypes.string, - navigationItemClicked: React.PropTypes.func.isRequired - }, +export default class Navigation extends React.PureComponent { + static propTypes = { + ast: PropTypes.object.isRequired, + activeSection: PropTypes.string, + navigationItemClicked: PropTypes.func.isRequired + } render() { var activeHeadings = []; let headings = this.props.ast.children @@ -91,6 +90,4 @@ var Navigation = React.createClass({ {footerContent} ); } -}); - -module.exports = Navigation; +} diff --git a/src/components/navigation_item.js b/src/components/navigation_item.js index db607d1..587d5a9 100644 --- a/src/components/navigation_item.js +++ b/src/components/navigation_item.js @@ -1,17 +1,16 @@ import React from 'react'; -import PureRenderMixin from 'react-pure-render/mixin'; +import PropTypes from 'prop-types'; -var NavigationItem = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - sectionName: React.PropTypes.string.isRequired, - active: React.PropTypes.bool.isRequired, - onClick: React.PropTypes.func.isRequired, - href: React.PropTypes.string.isRequired - }, - onClick() { +export default class NavigationItem extends React.PureComponent { + static propTypes = { + sectionName: PropTypes.string.isRequired, + active: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired, + href: PropTypes.string.isRequired + } + onClick = () => { this.props.onClick(this.props.sectionName); - }, + } render() { var {sectionName, href, active} = this.props; return (); } -}); - -module.exports = NavigationItem; +} diff --git a/src/components/rounded_toggle.js b/src/components/rounded_toggle.js index 44a3b05..f21d212 100644 --- a/src/components/rounded_toggle.js +++ b/src/components/rounded_toggle.js @@ -1,50 +1,46 @@ import React from 'react'; -import PureRenderMixin from 'react-pure-render/mixin'; +import PropTypes from 'prop-types'; -var roundedToggleOptionType = React.PropTypes.shape({ - title: React.PropTypes.string, - value: React.PropTypes.string +var roundedToggleOptionType = PropTypes.shape({ + title: PropTypes.string, + value: PropTypes.string }); -var RoundedToggle = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - options: React.PropTypes.arrayOf(roundedToggleOptionType).isRequired, +export default class RoundedToggle extends React.PureComponent { + static propTypes = { + options: PropTypes.arrayOf(roundedToggleOptionType).isRequired, active: roundedToggleOptionType, - short: React.PropTypes.bool, - onChange: React.PropTypes.func.isRequired - }, + short: PropTypes.bool, + onChange: PropTypes.func.isRequired + } render() { let { options, active } = this.props; return (
{options.map(option => - )} + className={`strong ${option.value === active.value ? 'active': ''}`} />))}
); } -}); +} -var RoundedToggleOption = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { +class RoundedToggleOption extends React.PureComponent { + static propTypes = { option: roundedToggleOptionType, - className: React.PropTypes.string.isRequired, - short: React.PropTypes.bool, - onClick: React.PropTypes.func.isRequired - }, - onClick() { + className: PropTypes.string.isRequired, + short: PropTypes.bool, + onClick: PropTypes.func.isRequired + } + onClick = () => { this.props.onClick(this.props.option); - }, + } render() { let { className, option } = this.props; return (
{this.props.short ? option.short : option.title}); } -}); - -module.exports = RoundedToggle; +} diff --git a/src/components/section.js b/src/components/section.js index f37b82c..0a3cb12 100644 --- a/src/components/section.js +++ b/src/components/section.js @@ -1,8 +1,8 @@ import React from 'react'; import remark from 'remark'; +import PropTypes from 'prop-types'; import remarkHTML from 'remark-html'; import remarkHighlight from '../highlight'; -import PureRenderMixin from 'react-pure-render/mixin'; import { postHighlight, remarkPlugins } from '../custom'; function renderHighlighted(nodes) { @@ -19,13 +19,12 @@ function renderHighlighted(nodes) { }; } -var Section = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - chunk: React.PropTypes.object.isRequired, - leftClassname: React.PropTypes.string.isRequired, - rightClassname: React.PropTypes.string.isRequired - }, +export default class Section extends React.PureComponent { + static propTypes = { + chunk: PropTypes.object.isRequired, + leftClassname: PropTypes.string.isRequired, + rightClassname: PropTypes.string.isRequired + } render() { let { chunk, leftClassname, rightClassname } = this.props; let { left, right, preview } = chunk; @@ -40,6 +39,4 @@ var Section = React.createClass({ dangerouslySetInnerHTML={renderHighlighted(right)} />} ); } -}); - -module.exports = Section; +}