react-accordion-with-header
Version:
React accordion component with flexbox header
164 lines (131 loc) • 6.14 kB
JavaScript
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
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; }
import React, { PureComponent, Children, cloneElement } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
var defaultProps = {
speed: 250
};
var defaultStyle = {
overflow: 'hidden',
padding: 0
};
var AccordionPanel = function (_PureComponent) {
_inherits(AccordionPanel, _PureComponent);
function AccordionPanel() {
var _temp, _this, _ret;
_classCallCheck(this, AccordionPanel);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _PureComponent.call.apply(_PureComponent, [this].concat(args))), _this), _this.state = {
panelHeight: 0
}, _this.preloadImages = function () {
var images = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var imagesLoaded = 0;
var imgLoaded = function imgLoaded(data) {
imagesLoaded++;
if (imagesLoaded === images.length) _this.calcHeight();
};
images.forEach(function (element) {
var img = new Image();
img.src = element.src;
img.onload = img.onerror = imgLoaded;
});
}, _this.calcHeight = function () {
if (_this.componentRef) {
var _panelHeight = _this.componentRef.current.clientHeight;
_this.setState({ panelHeight: _panelHeight });
return;
}
var panelHeight = Children.map(_this.props.children, function (child, i) {
return _this.refs['item-' + child.props.key];
}).reduce(function (previousValue, child) {
return previousValue + child.clientHeight;
}, _this.state.panelHeight);
_this.setState({ panelHeight: panelHeight });
}, _this.renderChildren = function () {
if (!_this.props.template && !_this.props.children) {
throw new Error('AccordionPanel must have at least one child!');
}
/***************************************************************
create a ref so we calculate its height on componentDidMount()
this way we know how high to expand the panel
***************************************************************/
var wrapComponent = function wrapComponent(components) {
return Children.map(components, function (child, index) {
var WrappedComponent = React.forwardRef(function (props, ref) {
_this.componentRef = ref;
return React.createElement(
'div',
{ ref: ref },
child
);
});
var ref = React.createRef();
return React.createElement(WrappedComponent, { ref: ref });
});
};
if (_this.props.template) {
// for template prop that contains a class component
console.warn('The template prop will be deprecated in the future. \n Prefer passing in your component directly as children: https://reactjs.org/docs/composition-vs-inheritance.html');
return wrapComponent(cloneElement(_this.props.template));
}
if (typeof _this.props.children.type === 'function') {
// for children that are class components
return wrapComponent(_this.props.children);
}
return Children.map(_this.props.children, function (child) {
return cloneElement(child, {
ref: 'item-' + child.props.key
});
});
}, _temp), _possibleConstructorReturn(_this, _ret);
}
AccordionPanel.prototype.componentDidMount = function componentDidMount() {
var bodyNode = this.accordionPanelRef;
if (!bodyNode) return; // tests only?
var images = bodyNode.querySelectorAll('img');
if (images.length > 0) {
this.preloadImages(images);
} else {
this.calcHeight();
}
};
// Wait for images to load before calculating height of element
AccordionPanel.prototype.render = function render() {
var _this2 = this;
var _props = this.props,
className = _props.className,
isExpanded = _props.isExpanded,
style = _props.style,
speed = _props.speed;
var styles = {
transition: 'all ' + (speed || defaultProps.speed) + 'ms ease-in-out',
maxHeight: isExpanded ? this.state.panelHeight : 0,
opacity: isExpanded ? 1 : 0
};
return React.createElement(
'div',
{
ref: function ref(inst) {
return _this2.accordionPanelRef = inst;
},
className: classNames(className, { 'is-expanded': isExpanded }),
style: _extends({}, style, defaultStyle, styles)
},
this.renderChildren()
);
};
return AccordionPanel;
}(PureComponent);
export { AccordionPanel as default };
AccordionPanel.propTypes = process.env.NODE_ENV !== "production" ? {
className: PropTypes.string,
style: PropTypes.object,
speed: PropTypes.number,
isExpanded: PropTypes.bool
} : {};
AccordionPanel.defaultProps = defaultProps;