commit c7eb6e8be971ae8466cf1b12dddc9cb0373638e4 parent 6e18aa146c4a02ba8d004ee12ae7392057608ea6 Author: Konstantin Tarkus <hello@tarkus.me> Date: Wed, 20 May 2015 20:52:03 +0300 Move higher-order components to src/decorators Diffstat:
18 files changed, 196 insertions(+), 195 deletions(-)
diff --git a/README.md b/README.md @@ -39,6 +39,7 @@ │ ├── /constants/ # Constants (action types etc.) │ ├── /content/ # Static content (plain HTML or Markdown, Jade, you name it) │ ├── /core/ # Core components (Flux dispatcher, base classes, utilities) +│ ├── /decorators/ # Higher-order React components │ ├── /stores/ # Stores contain the application state and logic │ ├── /templates/ # HTML templates for server-side rendering, emails etc. │ ├── /utils/ # Utility classes and functions diff --git a/src/components/App/App.js b/src/components/App/App.js @@ -2,8 +2,8 @@ import React, { PropTypes } from 'react'; import styles from './App.less'; // eslint-disable-line no-unused-vars -import withContext from '../decorators/withContext'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withContext from '../../decorators/withContext'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import AppActions from '../../actions/AppActions'; import AppStore from '../../stores/AppStore'; import Header from '../Header'; diff --git a/src/components/ContactPage/ContactPage.js b/src/components/ContactPage/ContactPage.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars import styles from './ContactPage.less'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars @withStyles(styles) class ContactPage { diff --git a/src/components/ContentPage/ContentPage.js b/src/components/ContentPage/ContentPage.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars import styles from './ContentPage.less'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars @withStyles(styles) class ContentPage { diff --git a/src/components/Feedback/Feedback.js b/src/components/Feedback/Feedback.js @@ -2,7 +2,7 @@ import React from 'react'; // eslint-disable-line no-unused-vars import styles from './Feedback.less'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars @withStyles(styles) class Feedback { diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js @@ -2,8 +2,8 @@ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars import styles from './Footer.less'; // eslint-disable-line no-unused-vars -import withViewport from '../decorators/withViewport'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withViewport from '../../decorators/withViewport'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import Link from '../../utils/Link'; @withViewport diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js @@ -2,7 +2,7 @@ import React from 'react'; // eslint-disable-line no-unused-vars import styles from './Header.less'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import Link from '../../utils/Link'; import Navigation from '../Navigation'; diff --git a/src/components/LoginPage/LoginPage.js b/src/components/LoginPage/LoginPage.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars import styles from './LoginPage.less'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars @withStyles(styles) class LoginPage { diff --git a/src/components/Navigation/Navigation.js b/src/components/Navigation/Navigation.js @@ -3,7 +3,7 @@ import React from 'react'; // eslint-disable-line no-unused-vars import classNames from 'classnames'; import styles from './Navigation.less'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import Link from '../../utils/Link'; @withStyles(styles) diff --git a/src/components/NotFoundPage/NotFoundPage.js b/src/components/NotFoundPage/NotFoundPage.js @@ -1,7 +1,7 @@ /*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import styles from './NotFoundPage.less'; // eslint-disable-line no-unused-vars @withStyles(styles) diff --git a/src/components/RegisterPage/RegisterPage.js b/src/components/RegisterPage/RegisterPage.js @@ -1,7 +1,7 @@ /*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import styles from './RegisterPage.less'; // eslint-disable-line no-unused-vars @withStyles(styles) diff --git a/src/components/TextBox/TextBox.js b/src/components/TextBox/TextBox.js @@ -1,7 +1,7 @@ /*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars -import withStyles from '../decorators/withStyles'; // eslint-disable-line no-unused-vars +import withStyles from '../../decorators/withStyles'; // eslint-disable-line no-unused-vars import styles from './TextBox.less'; // eslint-disable-line no-unused-vars @withStyles(styles) diff --git a/src/components/decorators/withContext.js b/src/components/decorators/withContext.js @@ -1,43 +0,0 @@ -/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ - -import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars -import emptyFunction from 'react/lib/emptyFunction'; - -function withContext(ComposedComponent) { - return class WithContext { - - static propTypes = { - context: PropTypes.shape({ - onInsertCss: PropTypes.func, - onSetTitle: PropTypes.func, - onSetMeta: PropTypes.func, - onPageNotFound: PropTypes.func - }) - }; - - static childContextTypes = { - onInsertCss: PropTypes.func.isRequired, - onSetTitle: PropTypes.func.isRequired, - onSetMeta: PropTypes.func.isRequired, - onPageNotFound: PropTypes.func.isRequired - }; - - getChildContext() { - let context = this.props.context; - return { - onInsertCss: context.onInsertCss || emptyFunction, - onSetTitle: context.onSetTitle || emptyFunction, - onSetMeta: context.onSetMeta || emptyFunction, - onPageNotFound: context.onPageNotFound || emptyFunction - }; - } - - render() { - let { context, ...other } = this.props; // eslint-disable-line no-unused-vars - return <ComposedComponent {...other} />; - } - - }; -} - -export default withContext; diff --git a/src/components/decorators/withStyles.js b/src/components/decorators/withStyles.js @@ -1,77 +0,0 @@ -/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ - -import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars -import invariant from 'react/lib/invariant'; -import { canUseDOM } from 'react/lib/ExecutionEnvironment'; - -let count = 0; - -function withStyles(styles) { - return (ComposedComponent) => class WithStyles { - - static contextTypes = { - onInsertCss: PropTypes.func - }; - - constructor() { - this.refCount = 0; - ComposedComponent.prototype.renderCss = function (css) { - let style; - if (canUseDOM) { - if (this.styleId && (style = document.getElementById(this.styleId))) { - if ('textContent' in style) { - style.textContent = css; - } else { - style.styleSheet.cssText = css; - } - } else { - this.styleId = `dynamic-css-${count++}`; - style = document.createElement('style'); - style.setAttribute('id', this.styleId); - style.setAttribute('type', 'text/css'); - - if ('textContent' in style) { - style.textContent = css; - } else { - style.styleSheet.cssText = css; - } - - document.getElementsByTagName('head')[0].appendChild(style); - this.refCount++; - } - } else { - this.context.onInsertCss(css); - } - }.bind(this); - } - - componentWillMount() { - if (canUseDOM) { - invariant(styles.use, `The style-loader must be configured with reference-counted API.`); - styles.use(); - } else { - this.context.onInsertCss(styles.toString()); - } - } - - componentWillUnmount() { - styles.unuse(); - if (this.styleId) { - this.refCount--; - if (this.refCount < 1) { - let style = document.getElementById(this.styleId); - if (style) { - style.parentNode.removeChild(style); - } - } - } - } - - render() { - return <ComposedComponent {...this.props} />; - } - - }; -} - -export default withStyles; diff --git a/src/components/decorators/withViewport.js b/src/components/decorators/withViewport.js @@ -1,62 +0,0 @@ -/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ - -import React, { Component } from 'react'; // eslint-disable-line no-unused-vars -import EventEmitter from 'eventemitter3'; -import { canUseDOM } from 'react/lib/ExecutionEnvironment'; - -let EE; -let viewport = {width: 1366, height: 768}; // Default size for server-side rendering -const RESIZE_EVENT = 'resize'; - -function handleWindowResize() { - if (viewport.width !== window.innerWidth || viewport.height !== window.innerHeight) { - viewport = {width: window.innerWidth, height: window.innerHeight}; - EE.emit(RESIZE_EVENT, viewport); - } -} - -function withViewport(ComposedComponent) { - return class WithViewport extends Component { - - static displayName = (ComposedComponent.displayName && ComposedComponent.displayName.indexOf('.') !== -1 ? - ComposedComponent.displayName.substr(0, ComposedComponent.displayName.indexOf('.')) : - ComposedComponent.name) + '.' + WithViewport.name; - - constructor() { - super(); - - this.state = { - viewport: canUseDOM ? {width: window.innerWidth, height: window.innerHeight} : viewport - }; - } - - componentDidMount() { - if (!EE) { - EE = new EventEmitter(); - window.addEventListener('resize', handleWindowResize); - window.addEventListener('orientationchange', handleWindowResize); - } - EE.on('resize', this.handleResize, this); - } - - componentWillUnmount() { - EE.removeListener(RESIZE_EVENT, this.handleResize, this); - if (!EE.listeners(RESIZE_EVENT, true)) { - window.removeEventListener('resize', handleWindowResize); - window.removeEventListener('orientationchange', handleWindowResize); - EE = null; - } - } - - render() { - return <ComposedComponent {...this.props} viewport={this.state.viewport}/>; - } - - handleResize(value) { - this.setState({viewport: value}); - } - - }; -} - -export default withViewport; diff --git a/src/decorators/withContext.js b/src/decorators/withContext.js @@ -0,0 +1,43 @@ +/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ + +import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars +import emptyFunction from '../../node_modules/react/lib/emptyFunction'; + +function withContext(ComposedComponent) { + return class WithContext { + + static propTypes = { + context: PropTypes.shape({ + onInsertCss: PropTypes.func, + onSetTitle: PropTypes.func, + onSetMeta: PropTypes.func, + onPageNotFound: PropTypes.func + }) + }; + + static childContextTypes = { + onInsertCss: PropTypes.func.isRequired, + onSetTitle: PropTypes.func.isRequired, + onSetMeta: PropTypes.func.isRequired, + onPageNotFound: PropTypes.func.isRequired + }; + + getChildContext() { + let context = this.props.context; + return { + onInsertCss: context.onInsertCss || emptyFunction, + onSetTitle: context.onSetTitle || emptyFunction, + onSetMeta: context.onSetMeta || emptyFunction, + onPageNotFound: context.onPageNotFound || emptyFunction + }; + } + + render() { + let { context, ...other } = this.props; // eslint-disable-line no-unused-vars + return <ComposedComponent {...other} />; + } + + }; +} + +export default withContext; diff --git a/src/decorators/withStyles.js b/src/decorators/withStyles.js @@ -0,0 +1,77 @@ +/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ + +import React, { PropTypes } from 'react'; // eslint-disable-line no-unused-vars +import invariant from '../../node_modules/react/lib/invariant'; +import { canUseDOM } from '../../node_modules/react/lib/ExecutionEnvironment'; + +let count = 0; + +function withStyles(styles) { + return (ComposedComponent) => class WithStyles { + + static contextTypes = { + onInsertCss: PropTypes.func + }; + + constructor() { + this.refCount = 0; + ComposedComponent.prototype.renderCss = function (css) { + let style; + if (canUseDOM) { + if (this.styleId && (style = document.getElementById(this.styleId))) { + if ('textContent' in style) { + style.textContent = css; + } else { + style.styleSheet.cssText = css; + } + } else { + this.styleId = `dynamic-css-${count++}`; + style = document.createElement('style'); + style.setAttribute('id', this.styleId); + style.setAttribute('type', 'text/css'); + + if ('textContent' in style) { + style.textContent = css; + } else { + style.styleSheet.cssText = css; + } + + document.getElementsByTagName('head')[0].appendChild(style); + this.refCount++; + } + } else { + this.context.onInsertCss(css); + } + }.bind(this); + } + + componentWillMount() { + if (canUseDOM) { + invariant(styles.use, `The style-loader must be configured with reference-counted API.`); + styles.use(); + } else { + this.context.onInsertCss(styles.toString()); + } + } + + componentWillUnmount() { + styles.unuse(); + if (this.styleId) { + this.refCount--; + if (this.refCount < 1) { + let style = document.getElementById(this.styleId); + if (style) { + style.parentNode.removeChild(style); + } + } + } + } + + render() { + return <ComposedComponent {...this.props} />; + } + + }; +} + +export default withStyles; diff --git a/src/decorators/withViewport.js b/src/decorators/withViewport.js @@ -0,0 +1,62 @@ +/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */ + +import React, { Component } from 'react'; // eslint-disable-line no-unused-vars +import EventEmitter from 'eventemitter3'; +import { canUseDOM } from '../../node_modules/react/lib/ExecutionEnvironment'; + +let EE; +let viewport = {width: 1366, height: 768}; // Default size for server-side rendering +const RESIZE_EVENT = 'resize'; + +function handleWindowResize() { + if (viewport.width !== window.innerWidth || viewport.height !== window.innerHeight) { + viewport = {width: window.innerWidth, height: window.innerHeight}; + EE.emit(RESIZE_EVENT, viewport); + } +} + +function withViewport(ComposedComponent) { + return class WithViewport extends Component { + + static displayName = (ComposedComponent.displayName && ComposedComponent.displayName.indexOf('.') !== -1 ? + ComposedComponent.displayName.substr(0, ComposedComponent.displayName.indexOf('.')) : + ComposedComponent.name) + '.' + WithViewport.name; + + constructor() { + super(); + + this.state = { + viewport: canUseDOM ? {width: window.innerWidth, height: window.innerHeight} : viewport + }; + } + + componentDidMount() { + if (!EE) { + EE = new EventEmitter(); + window.addEventListener('resize', handleWindowResize); + window.addEventListener('orientationchange', handleWindowResize); + } + EE.on('resize', this.handleResize, this); + } + + componentWillUnmount() { + EE.removeListener(RESIZE_EVENT, this.handleResize, this); + if (!EE.listeners(RESIZE_EVENT, true)) { + window.removeEventListener('resize', handleWindowResize); + window.removeEventListener('orientationchange', handleWindowResize); + EE = null; + } + } + + render() { + return <ComposedComponent {...this.props} viewport={this.state.viewport}/>; + } + + handleResize(value) { + this.setState({viewport: value}); + } + + }; +} + +export default withViewport;