diff --git a/README.md b/README.md index 24405e0..f626bff 100644 --- a/README.md +++ b/README.md @@ -21,39 +21,39 @@ This module is pretty straightforward: You specify a set of requirements, and th ## Usage -A mq element functions like any other React component, which means you can nest them and do all the normal jazz. +A MediaQuery element functions like any other React component, which means you can nest them and do all the normal jazz. ### Using CSS Media Queries -```js -var mq = require('react-responsive'); +```jsx +var MediaQuery = require('react-responsive'); var A = React.createClass({ render: function(){ return (
Device Test!
- +
You are a desktop or laptop
- +
You also have a huge screen
-
- +
+
You are sized like a tablet or mobile phone though
-
- - + + +
You are a tablet or mobile phone
-
- + +
You are portrait
-
- + +
You are landscape
-
- + +
You are retina
-
+
); } @@ -71,35 +71,81 @@ For a list of all possible shorthands and value types see https://github.com/wea Any numbers given as a shorthand will be expanded to px (`1234` will become `'1234px'`) -```js -var mq = require('react-responsive'); +```jsx +var MediaQuery = require('react-responsive'); var A = React.createClass({ render: function(){ return (
Device Test!
- +
You are a desktop or laptop
- +
You also have a huge screen
-
- +
+
You are sized like a tablet or mobile phone though
-
- - + + +
You are a tablet or mobile phone
-
- + +
You are portrait
-
- + +
You are landscape
-
- + +
You are retina
-
+ +
+ ); + } +}); +``` + +### Server rendering + +Server rendering can be done by passing static values through the `values` property. + +The values property can contain `orientation`, `scan`, `aspectRatio`, `deviceAspectRatio`, +`height`, `deviceHeight`, `width`, `deviceWidth`, `color`, `colorIndex`, `monochrome`, + `resolution` and `type` to be matched against the media query. + +`type` can be one of: `all`, `grid`, `aural`, `braille`, `handheld`, `print`, `projection`, +`screen`, `tty`, `tv` or `embossed`. + +```jsx +var MediaQuery = require('react-responsive'); + +var A = React.createClass({ + render: function(){ + return ( +
+
Device Test!
+ +
You are a desktop or laptop
+ +
You also have a huge screen
+
+ +
You are sized like a tablet or mobile phone though
+
+
+ +
You are a tablet or mobile phone
+
+ +
You are portrait
+
+ +
You are landscape
+
+ +
You are retina
+
); } @@ -145,4 +191,4 @@ Pretty much everything. Check out these polyfills: [downloads-image]: http://img.shields.io/npm/dm/react-responsive.svg [npm-url]: https://npmjs.org/package/react-responsive -[npm-image]: http://img.shields.io/npm/v/react-responsive.svg \ No newline at end of file +[npm-image]: http://img.shields.io/npm/v/react-responsive.svg diff --git a/gulpfile.js b/gulpfile.js index 4d48ab4..a03a751 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -42,6 +42,15 @@ var sampleBundler = watchify(browserify('./samples/sandbox/src/index.jsx', { })); sampleBundler.transform(reactify); +var staticSampleBundler = watchify(browserify('./samples/static/src/index.jsx', { + cache: bundleCache, + packageCache: pkgCache, + fullPaths: true, + standalone: 'sample', + debug: true +})); +staticSampleBundler.transform(reactify); + gulp.task('watch', function(){ bundler.on('update', function(){ gulp.start('js'); @@ -79,11 +88,20 @@ gulp.task('samples', function(){ .pipe(gulp.dest('samples/sandbox/dist')) .pipe(lr()); + var browserifyStream2 = staticSampleBundler.bundle() + // browserify -> gulp transfer + .pipe(source('index.js')) + .pipe(buffer()) + .pipe(cached('index')) + .pipe(sourcemaps.init({loadMaps: true})) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest('samples/static/dist')); + var staticStream = gulp.src(['samples/sandbox/src/**/*', '!samples/sandbox/src/**/*.js']) .pipe(cached('static-samples')) .pipe(gulp.dest('samples/sandbox/dist')); - return merge(staticStream, browserifyStream); + return merge(staticStream, browserifyStream, browserifyStream2); }); gulp.task('sample-server', function(cb){ diff --git a/package.json b/package.json index 3ba3db5..ebeffe0 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ ], "dependencies": { "lodash.omit": "^3.0.0", + "matchmedia": "^0.1.1", "object-assign": "^2.0.0" }, "peerDependencies": { @@ -43,7 +44,7 @@ "jshint-stylish": "^0.4.0", "merge-stream": "^0.1.5", "mocha": "^1.20.1", - "reactify": "^0.14.0", + "reactify": "^1.0.0", "should": "^4.0.4", "vinyl-buffer": "0.0.0", "vinyl-source-stream": "^0.1.1", diff --git a/samples/sandbox/src/index.jsx b/samples/sandbox/src/index.jsx index 5217022..753956b 100644 --- a/samples/sandbox/src/index.jsx +++ b/samples/sandbox/src/index.jsx @@ -1,9 +1,8 @@ -/** @jsx React.DOM */ /* global document, window */ 'use strict'; -var mq = require('../../../src'); +var MediaQuery = require('../../../src'); var React = require('react'); window.React = React; // for dev @@ -13,31 +12,31 @@ var App = React.createClass({ return (
Device Test!
- +
You are a desktop or laptop
- +
You also have a huge screen
-
- +
+
You are sized like a tablet or mobile phone though
-
- - + + +
You are a tablet or mobile phone
-
+ - +
You are portrait
-
- + +
You are landscape
-
- + +
You are retina
-
+
); } }); -React.renderComponent(App(), document.body); \ No newline at end of file +React.renderComponent(App(), document.body); diff --git a/samples/static/src/index.jsx b/samples/static/src/index.jsx new file mode 100644 index 0000000..0a8ceb6 --- /dev/null +++ b/samples/static/src/index.jsx @@ -0,0 +1,39 @@ +'use strict'; + +var MediaQuery = require('../../../src'); +var React = require('react'); + +var App = React.createClass({ + displayName: 'demo', + render: function(){ + return ( +
+
Device Test!
+ +
You are a desktop or laptop
+ +
You also have a huge screen
+
+ +
You are sized like a tablet or mobile phone though
+
+
+ +
You are a tablet or mobile phone
+
+ + +
You are portrait
+
+ +
You are landscape
+
+ +
You are retina
+
+
+ ); + } +}); + +console.log(React.renderToString()); diff --git a/src/index.js b/src/index.js index 727455a..869860f 100644 --- a/src/index.js +++ b/src/index.js @@ -4,13 +4,15 @@ var React = require('react'); var omit = require('lodash.omit'); +var matchMedia = require('matchmedia'); +var hyphenate = require('react/lib/hyphenateStyleName'); var mediaQuery = require('./mediaQuery'); var toQuery = require('./toQuery'); -var matchMedia = typeof window !== 'undefined' ? window.matchMedia : null; var defaultTypes = { - component: React.PropTypes.func, - query: React.PropTypes.string + component: React.PropTypes.node, + query: React.PropTypes.string, + values: React.PropTypes.shape(mediaQuery.matchers) }; var mediaKeys = Object.keys(mediaQuery.all); var excludedQueryKeys = Object.keys(defaultTypes); @@ -21,7 +23,8 @@ var mq = React.createClass({ getDefaultProps: function(){ return { - component: React.DOM.div + component: 'div', + values: {} }; }, @@ -40,6 +43,7 @@ var mq = React.createClass({ }, updateQuery: function(props){ + var values; if (props.query) { this.query = props.query; } else { @@ -49,7 +53,16 @@ var mq = React.createClass({ if (!this.query) { throw new Error('Invalid or missing MediaQuery!'); } - this._mql = matchMedia(this.query); + + if (props.values) { + values = Object.keys(props.values) + .reduce(function(result, key){ + result[hyphenate(key)] = props.values[key]; + return result; + }, {}); + } + + this._mql = matchMedia(this.query, values); this._mql.addListener(this.updateMatches); this.updateMatches(); }, @@ -72,7 +85,7 @@ var mq = React.createClass({ return null; } var props = omit(this.props, excludedPropKeys); - return this.props.component(props, this.props.children); + return React.createElement(this.props.component, props, this.props.children); } }); diff --git a/src/mediaQuery.js b/src/mediaQuery.js index a002f06..b774e69 100644 --- a/src/mediaQuery.js +++ b/src/mediaQuery.js @@ -6,8 +6,8 @@ var stringOrNumber = PropTypes.oneOfType([ PropTypes.number ]); -var features = { - // media features +// properties that match media queries +var matchers = { orientation: PropTypes.oneOf([ 'portrait', 'landscape' @@ -19,45 +19,57 @@ var features = { ]), aspectRatio: PropTypes.string, + deviceAspectRatio: PropTypes.string, + + height: stringOrNumber, + deviceHeight: stringOrNumber, + + width: stringOrNumber, + deviceWidth: stringOrNumber, + + color: PropTypes.bool, + + colorIndex: PropTypes.bool, + + monochrome: PropTypes.bool, + resolution: stringOrNumber +}; + +// media features +var features = { minAspectRatio: PropTypes.string, maxAspectRatio: PropTypes.string, - deviceAspectRatio: PropTypes.string, minDeviceAspectRatio: PropTypes.string, maxDeviceAspectRatio: PropTypes.string, - height: stringOrNumber, minHeight: stringOrNumber, maxHeight: stringOrNumber, - deviceHeight: stringOrNumber, minDeviceHeight: stringOrNumber, maxDeviceHeight: stringOrNumber, - width: stringOrNumber, minWidth: stringOrNumber, maxWidth: stringOrNumber, - deviceWidth: stringOrNumber, minDeviceWidth: stringOrNumber, maxDeviceWidth: stringOrNumber, - color: PropTypes.bool, minColor: PropTypes.number, maxColor: PropTypes.number, - colorIndex: PropTypes.bool, minColorIndex: PropTypes.number, maxColorIndex: PropTypes.number, - monochrome: PropTypes.bool, minMonochrome: PropTypes.number, maxMonochrome: PropTypes.number, - resolution: stringOrNumber, minResolution: stringOrNumber, maxResolution: stringOrNumber }; +assign(features, matchers); + // media types var types = { + all: PropTypes.bool, grid: PropTypes.bool, aural: PropTypes.bool, braille: PropTypes.bool, @@ -74,8 +86,12 @@ var all = {}; assign(all, types); assign(all, features); +// add the type property +assign(matchers, { type: Object.keys(types) }); + module.exports = { all: all, types: types, + matchers: matchers, features: features };