-
-
Save jkneal/23aee729dd20410af7a6 to your computer and use it in GitHub Desktop.
React Element Query
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const React = require('react') | |
const {ElementQueryMixin} = require('../helpers/element-query') | |
require('./card.styl') | |
module.exports = { | |
title: 'Element Queries', | |
component: React.createClass({ | |
displayName: 'ElementQueryDemo', | |
render() { | |
return (<div style={{marginBottom: 50}}> | |
<ElementQueryStyleDemo/> | |
<ElementQueryTableDemo/> | |
<ElementQueryCardDemo/> | |
</div> | |
) | |
} | |
}) | |
} | |
const ElementQueryStyleDemo = React.createClass({ | |
displayName: 'ElementQueryStyleDemo', | |
render() { | |
return (<div> | |
<h2>Changing Styles</h2> | |
<p>Colors change ever 200px below 900px</p> | |
<ColorBar/> | |
<p>Color bar in 400px container</p> | |
<div style={{width: 400}}> | |
<ColorBar/> | |
</div> | |
</div> | |
) | |
} | |
}) | |
const ColorBar = React.createClass({ | |
displayName: 'ColorBar', | |
mixins: [ElementQueryMixin], | |
render() { | |
let color = 'blue' | |
if (this.matchMedia('(min-width: 701px) and (max-width: 900px)')) { | |
color = 'red' | |
} | |
else if (this.matchMedia('(min-width: 501px) and (max-width: 700px)')) { | |
color = 'green' | |
} | |
else if (this.matchMedia('(min-width: 301px) and (max-width: 500px)')) { | |
color = 'orange' | |
} | |
const style = { | |
width: '100%', | |
height: '100px', | |
color: 'white', | |
padding: 30, | |
backgroundColor: color | |
} | |
return (<div> | |
<div style={style}>Watch the colors!</div> | |
{this.getEQSensor()} | |
</div> | |
) | |
} | |
}) | |
const ElementQueryTableDemo = React.createClass({ | |
displayName: 'ElementQueryTableDemo', | |
mixins: [ElementQueryMixin], | |
render() { | |
return (<div> | |
<h2>Responsive Table</h2> | |
<p>Break point at 900px</p> | |
{this.matchMedia('(min-width: 901px)') && <table> | |
<thead><th>Header 1</th><th>Header 2</th> | |
<th>Header 3</th></thead> | |
<tr><td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</td> | |
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</td> | |
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</td> | |
</tr> | |
</table>} | |
{this.matchMedia('(max-width: 900px)') && <ul> | |
<li>Header 1 - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li> | |
<li>Header 2 - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li> | |
<li>Header 3 - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</li> | |
</ul>} | |
{this.getEQSensor()} | |
</div> | |
) | |
} | |
}) | |
const ElementQueryCardDemo = React.createClass({ | |
displayName: 'ElementQueryCardDemo', | |
render() { | |
return (<div> | |
<h2>Responsive Card</h2> | |
<p>Large image appears when card is larger than 400px</p> | |
<div style={{width: '50%', float: 'left', marginRight: 30}}> | |
<KittyCard/> | |
</div> | |
<div style={{width: '200px', float: 'right'}}> | |
<KittyCard/> | |
</div> | |
</div> | |
) | |
} | |
}) | |
const KittyCard = React.createClass({ | |
displayName: 'KittyCard', | |
mixins: [ElementQueryMixin], | |
render() { | |
return (<div className="card primary"> | |
{this.matchMedia('(min-width: 400px)') && | |
<img style={{width: '100%'}} src="http://placekitten.com/g/400/200"/>} | |
<div className="card-divider"> | |
{this.matchMedia('(max-width: 399px)') && | |
<img style={{width: 25, height: 15, marginRight: 10}} src="http://placekitten.com/g/400/200"/>} | |
I'm so cute! | |
</div> | |
<div className="card-section"> | |
<h4>Look at This Swag Card</h4> | |
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi iusto reprehenderit | |
voluptatem odio deleniti provident aliquam qui magnam aspernatur necessitatibus.</p> | |
</div> | |
{this.getEQSensor()} | |
</div> | |
) | |
} | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const React = require('react') | |
const {forIn} = require('lodash') | |
/** | |
* Mixin which provides element queries for the component. | |
* | |
* Call matchMedia to run an element query. This will setup a listener that will update the component's | |
* state whenever the query result changes. | |
* | |
* @see http://www.backalleycoder.com/2014/04/18/element-queries-from-the-feet-up/ | |
*/ | |
export const ElementQueryMixin = { | |
getInitialState() { | |
return { | |
queries: [], | |
mqls: {} | |
} | |
}, | |
matchMedia(query) { | |
if (this.state.mqls[query]) { | |
return this.state.mqls[query].matches | |
} | |
// since this can be called during render and we can't update state, need to | |
// run on next tick. Adding a little delay due to quirk with firefox | |
setTimeout(() => { | |
if (this.isMounted()) { | |
this.runQuery(query) | |
} | |
else { | |
// hold query for evaluation on component mount | |
let queries = this.state.queries | |
queries.push(query) | |
this.setState({queries: queries}) | |
} | |
}, 10) | |
return false | |
}, | |
runQuery(query) { | |
const probeNode = this.refs.sensor.getDOMNode() | |
if (probeNode.contentDocument) { | |
const window = probeNode.contentDocument.defaultView | |
const mql = window.matchMedia(query) | |
mql.addListener(this.handleMediaChange(query)) | |
this.saveMediaQueryList(query, mql) | |
} | |
}, | |
saveMediaQueryList(query, mql) { | |
let mqls = this.state.mqls | |
mqls[query] = mql | |
this.setState({mqls: mqls}) | |
}, | |
handleMediaChange(query) { | |
return (mql) => this.saveMediaQueryList(query, mql) | |
}, | |
componentDidMount() { | |
this.state.queries.forEach(query => this.runQuery(query)) | |
}, | |
componentWillUnmount() { | |
forIn(this.state.mqls, (mql, query) => mql.removeListener(this.handleMediaChange(query))) | |
}, | |
getEQSensor() { | |
return <object ref="sensor" style={{width: '100%', height: 0}} type="text/html" data="about:blank"> | |
</object> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment