Skip to content

Instantly share code, notes, and snippets.

@slorber
Created August 22, 2016 09:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slorber/27006e27997464c9f737ac22a8a89a94 to your computer and use it in GitHub Desktop.
Save slorber/27006e27997464c9f737ac22a8a89a94 to your computer and use it in GitHub Desktop.
'use strict';
import React, { PropTypes } from 'react';
import $ from 'jquery';
import _ from 'lodash';
import shallowCompare from 'react-addons-shallow-compare';
const ResponsiveContainer = React.createClass({
propTypes: {
containerClassName: React.PropTypes.string,
component: React.PropTypes.func.isRequired,
mapDimensionsToProps: React.PropTypes.func.isRequired
},
getInitialState() {
return {responsiveProps: undefined}
},
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
},
componentDidMount() {
this.handleResize = _.throttle(this.handleResize,200);
$(window).on("resize",this.handleResize);
this.updateDimensions();
},
componentWillUnmount() {
$(window).off("resize",this.handleResize);
},
componentDidUpdate() {
this.updateDimensions();
},
handleResize() {
this.updateDimensions();
},
updateDimensions() {
const container = this._container;
if (!container) {
return; // I think it happens when hot reloading code it's weird (?)
}
const newDimensions = {
containerWidth: $(container).width(),
containerHeight: $(container).height()
};
// TODO a bit dirty optimization logic, reimplement with proper shouldComponentUpdate?
if ( !_.isEqual(this.dimensions,newDimensions) ) {
this.dimensions = newDimensions;
const newResponsiveProps = this.props.mapDimensionsToProps(newDimensions);
if ( !_.isEqual(this.state.responsiveProps,newResponsiveProps) ) {
this.setState({responsiveProps: newResponsiveProps});
}
}
},
maybeRenderChildren() {
if ( this.state.responsiveProps ) {
const Comp = this.props.component;
return <Comp
{..._.omit(this.props,["component","containerClassName","mapDimensionsToProps"])}
{...this.state.responsiveProps}
/>;
}
},
render() {
return (
<div ref={c => this._container = c} className={this.props.containerClassName}>
{this.maybeRenderChildren()}
</div>
)
}
});
exports.injectColumnNumber = ({minColumnWidth,minColumnNumber,maxColumnNumber,containerClassName}) => {
const mapDimensionsToProps = ({containerWidth}) => {
let columnNumber = Math.trunc( containerWidth / minColumnWidth );
if ( minColumnNumber && columnNumber < minColumnNumber ) columnNumber = minColumnNumber;
if ( maxColumnNumber && columnNumber > maxColumnNumber ) columnNumber = maxColumnNumber;
return {
columnNumber: columnNumber
}
};
return Component => React.createClass({
render() {
return <ResponsiveContainer
{...this.props}
component={Component}
containerClassName={containerClassName}
mapDimensionsToProps={mapDimensionsToProps}
/>
}
});
};
exports.injectContainerWidthRange = ({containerClassName,propName,config}) => {
if ( Object.keys(config).length === 0 ) {
throw new Error("ranges are not provided");
}
let ranges = Object.keys(config).map( key => ({name: key, width: config[key]}) );
ranges = _.sortBy(ranges,range => range.width);
if ( ranges[0].width !== 0 ) {
throw new Error("There should be at least a named range starting with width = 0");
}
const mapDimensionsToProps = ({containerWidth}) => {
const width = containerWidth;
const range =_.findLast(ranges,range => width >= range.width);
const result = {};
const finalPropName = propName || "containerWidthRange";
result[finalPropName] = range.name;
return result;
};
return Component => React.createClass({
render() {
return <ResponsiveContainer
{...this.props}
component={Component}
containerClassName={containerClassName}
mapDimensionsToProps={mapDimensionsToProps}
/>
}
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment