diff --git a/README.md b/README.md index 3ead81b..03ddf51 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,122 @@ -# Responsive Component +# React ResponsiveRenderIf -A responsive React component that takes a media query and renders its children only if the query matches. Handles changes if you resize your browser or flip your device... +A responsive React component that takes a media query and renders its children only if the query matches. Handles changes if you resize your browser or flip your device. -## Implementation +### Implementation -Works natively with `Window.matchmedia` api that takes a media query and returns a `MediaQueryList` object representing the parsed result. [read more](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) +Works natively with `Window.matchMedia` api that takes a media query and returns a `MediaQueryList` object representing the parsed result. [read more](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) -| Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari | -|:----------:|:-------------:|:------:|:-------------:|:------:|:-------------:| -| Basic support | 9 | 6.0 (6.0) | 10 | 12.1 | 5.1 | +It works natively with the following browsers: +| Chrome | Firefox | Internet Explorer | Opera | Safari | Safari Mobile | Android | +|:----------:|:-------------:|:------:|:-------------:|:------:|:-------------:|:-----:|:-----:| +| 9 | 6.0 | 10 | 12.1 | 5.1 | 3.0 | 5 | For older browsers, it falls back to Paul Irish's implementation of `matchMedia`: [read more](https://github.com/paulirish/matchMedia.js/) ### How to use this awesome piece of code? -[Try it out on codepen](http://codepen.io/jfkhoury/pen/MegmwV) - -All you need to do is wrap your content (React Components or jsx or html...) for the specific screen/device you are trying to target, and set the media query accordingly: +First install it. +``` +$ npm i react-responsive-render-if --save +``` +Then `import` into your code as follows (you _are_ using ES6, right? :smirk:): ```javascript - import { ResponsiveComponent } from "react-responsive-component"; - ... - - - +import ResponsiveRenderIf from "react-responsive-render-if"; +``` + +Then all you need to do is wrap your content (JSX or HTML) +and set the `query` for the specific media you are trying to target. +You can use any media query supported by your browser. For a complete listing +see the [Media Queries Level 4](https://drafts.csswg.org/mediaqueries/) spec. + +Here we display a different message depending on your screen width: +```html + + You are reading this on a narrow browser window or a mobile device. + - - - + + Ahh, look at all of this screen real estate! + ``` -#### Optional props +Or how about this cool trick. You don't want some area of your app to be printed? No problem. +```html + + You are reading this on your screen. + -You can pass a `tag` props to specify the tagname of the responsive component wrapper. + + Sorry, but you can't print this. + +``` -```javascript - -
  • This feature is not supported on TVs yet :(
  • -
    +### Optional props + +#### `masqueradeAs` + +You can pass a `masqueradeAs` to specify the component to "masquerade as" (i.e. to render as). +If you don't, ResponsiveRenderIf will render a "div", but only when necessary. +'masqueradeAs' can either be a string (such as "div" or "ul"), or a component (like "MyButton"). + +Let's say you had a component that you used as follows: +```html + +``` +The following will render the same output, but only when not bing printed +(who needs to print a button on the printer anyway?) +```html + +``` + +If you don't specify a `masqueradeAs` AND don't specify additional props AND +there is but a single child element, ResponsiveRenderIf adds no additional markup! In other words, +you pay no price (DOM wise) for using ResponsiveRenderIf. :tada: + +For example, the following will NOT render a wrapping div as it wraps a single child element: +```html + +
    +

    It was the best of times.

    +

    It was the worst of times.

    +
    +
    +``` +but this will: +```html + +

    It was the best of times.

    +

    It was the worst of times.

    +
    +``` +Actually, the two examples above will render the exact same output. The second will wrap the multiple children +masquerading as a "div". + +You can even pass additional props to the masqueraded component: +```html + +
  • This is item four
  • +
  • This is item five
  • +
    +``` +which will render the following: +```html +
      +
    1. This is item four
    2. +
    3. This is item five
    4. +
    +``` + +### Try it out Live! +See it live and in action [on codepen](http://codepen.io/jfkhoury/pen/MegmwV). + +### More Examples + +What if your code works best when displayed in landscape mode? +```html + + Please turn your device for optimal viewing + ``` diff --git a/package.json b/package.json index c36c415..2920af1 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "version": "1.0.8", - "name": "react-responsive-component", + "version": "2.0.0-alpha.1", + "name": "react-responsive-render-if", "description": "A responsive React component that reacts to media queries.", - "keywords": ["react", "reactjs", "responsive"], + "keywords": ["react", "reactjs", "responsive", "react-component"], "main": "./lib/index.js", "scripts": { "prepublish": "npm run lint && npm run build", diff --git a/src/index.js b/src/index.js index b9a8324..550b52f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import "matchmedia-polyfill"; import "matchmedia-polyfill/matchMedia.addListener"; -import ResponsiveComponent from "./ui/ResponsiveComponent"; +import ResponsiveRenderIf from "./ui/ResponsiveRenderIf"; -export { ResponsiveComponent }; +export default ResponsiveRenderIf; diff --git a/src/ui/ResponsiveComponent.js b/src/ui/ResponsiveRenderIf.js similarity index 51% rename from src/ui/ResponsiveComponent.js rename to src/ui/ResponsiveRenderIf.js index e862c45..313bb99 100644 --- a/src/ui/ResponsiveComponent.js +++ b/src/ui/ResponsiveRenderIf.js @@ -1,6 +1,6 @@ import React from "react"; -export default class ResponsiveComponent extends React.Component { +export default class ResponsiveRenderIf extends React.Component { constructor(props) { super(props); this.state = { canRender: false }; @@ -22,7 +22,7 @@ export default class ResponsiveComponent extends React.Component { _tryToRender(props = this.props) { if (!props.query) { - throw new Error("ResponsiveComponent: `query` is a required prop!"); + throw new Error("ResponsiveRenderIf: `query` is a required prop!"); } this._dispose(); @@ -44,22 +44,30 @@ export default class ResponsiveComponent extends React.Component { } } - get _display() { - return React.createElement(this.props.tag, - { className: "responsive-component" }, - this.props.children); + _render() { + const { masqueradeAs, query, children, ...additionProps } = this.props; + const multipleChildren = children.length > 1; + const hasAdditionProps = Object.keys(additionProps).length; + + if (multipleChildren || masqueradeAs || hasAdditionProps) { + const Component = masqueradeAs || "div"; + return {children}; + } else { + return this.props.children; // Render the single child without a wrapper. + } } render() { - return this.state.canRender ? this._display : null; + return this.state.canRender ? this._render() : null; } } -ResponsiveComponent.propTypes = { - query: React.PropTypes.string.isRequired, - tag: React.PropTypes.string -}; - -ResponsiveComponent.defaultProps = { - tag: "div" -}; +if (process.env.NODE_ENV !== "production") { + ResponsiveRenderIf.propTypes = { + query: React.PropTypes.string.isRequired, + masqueradeAs: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element + ]) + }; +}