react-pinterest2
Version:
Collection of embeddable Pinterest buttons and widgets
220 lines (181 loc) • 8.44 kB
JavaScript
;
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
Object.defineProperty(exports, "__esModule", {
value: true
});
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _PinterestBase2 = require('./PinterestBase');
var _PinterestBase3 = _interopRequireDefault(_PinterestBase2);
var _PinUtil = require('../util/PinUtil');
var _PinUtil2 = _interopRequireDefault(_PinUtil);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* This is the classic Pinterest "masonry" grid. It lays out all of the
* child nodes in columns, fitting them into the shortest available
* column for each child.
* *** GOTCHA ***
* If the child component is a custom React component, it must apply its
* <style> prop to the root node of the component.
*/
var PinterestGrid = (function (_PinterestBase) {
_inherits(PinterestGrid, _PinterestBase);
function PinterestGrid(props) {
_classCallCheck(this, PinterestGrid);
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(PinterestGrid).call(this, props));
_this.state = { styles: [] };
return _this;
}
_createClass(PinterestGrid, [{
key: 'componentDidMount',
value: function componentDidMount() {
if (this.props.children.length) {
this.layout();
}
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
this.layout();
}
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(nextProps, nextState) {
return this.props.children.length || this.props.children.length !== nextProps.children.length || this.state.styles.length !== nextState.styles.length;
}
/**
* Build a style attribute based on passed in styles
* and the opacity signifying readiness
* @returns {object} style key/value map
*/
}, {
key: 'getStyle',
value: function getStyle() {
return _PinUtil2.default.extend({
opacity: this.state.styles.length ? 1 : 0
}, this.props.style);
}
/**
* Find which column is the shortest
* @param {array} a list of column heights
* @returns {number} the index of the column to use
*/
}, {
key: 'getShortestColumn',
value: function getShortestColumn(columns) {
var shortest = columns.reduce(function (smallest, columnHeight) {
return columnHeight < smallest ? columnHeight : smallest;
});
return columns.indexOf(shortest);
}
/**
* Look at the root node and/or its parent, and determine
* how many columns we can fit.
* @returns {number} the number of columns to use
*/
}, {
key: 'getColumnCount',
value: function getColumnCount() {
if (this.props.columns) {
return this.props.columns;
} else {
var rootNode = _react2.default.findDOMNode(this.refs.root);
var rootWidth = rootNode.offsetWidth || rootNode.parentNode.offsetWidth;
var childNode = _react2.default.findDOMNode(this.refs['child-0']);
var childWidth = childNode.offsetWidth;
return Math.floor(rootWidth / (childWidth + this.props.gutter));
}
}
/**
* Wait for children to render, and then determine each element's
* absolute positioning within the grid
* @returns {Promise}
*/
}, {
key: 'layout',
value: function layout() {
var _this2 = this;
this.waitForChildren().then(function () {
var columnCount = _this2.getColumnCount();
var gutter = _this2.props.gutter;
var nodeWidth = _react2.default.findDOMNode(_this2.refs['child-0']).offsetWidth;
var columnHeights = Array.apply(null, Array(columnCount)).map(function (x) {
return 0;
});
var styles = _this2.props.children.map(function (child, i) {
var node = _react2.default.findDOMNode(_this2.refs['child-' + i]);
var columnIndex = _this2.getShortestColumn(columnHeights);
var top = columnHeights[columnIndex];
var left = columnIndex * (nodeWidth + gutter);
columnHeights[columnIndex] += node.offsetHeight + gutter;
return {
position: 'absolute',
top: top + 'px',
left: left + 'px'
};
});
_this2.setState({ styles: styles });
});
}
/**
* Wait for all children to have been rendered
* @returns {Promise}
*/
}, {
key: 'waitForChildren',
value: function waitForChildren() {
var _this3 = this;
return new Promise(function (resolve) {
var interval = setInterval(function () {
var ready = _this3.props.children.every(function (child, i) {
var node = _react2.default.findDOMNode(_this3.refs['child-' + i]);
return _react2.default.findDOMNode(_this3.refs['child-' + i]);
});
if (ready) {
clearInterval(interval);
resolve();
}
}, 50);
});
}
/**
* Build out the child nodes with the additional style and ref attributes
* @returns {array} a list of ready-to-render child nodes
*/
}, {
key: 'getUpdatedChildren',
value: function getUpdatedChildren() {
var _this4 = this;
return _react2.default.Children.map(this.props.children, function (child, i) {
var style = child.props.style || {};
return _react2.default.cloneElement(child, {
ref: 'child-' + i,
style: _PinUtil2.default.extend({}, _this4.state.styles[i], child.props.style)
});
});
}
/**
* Render the children absolutely positioned within parent
*/
}, {
key: 'render',
value: function render() {
return _react2.default.createElement(
'div',
{ ref: 'root', className: 'pinterest-grid', style: this.getStyle() },
this.getUpdatedChildren()
);
}
}]);
return PinterestGrid;
})(_PinterestBase3.default);
exports.default = PinterestGrid;
PinterestGrid.propTypes = {
gutter: _react2.default.PropTypes.number
};
PinterestGrid.defaultProps = {
gutter: 0
};