react-accordion-with-header
Version:
React accordion component with flexbox header
182 lines (153 loc) • 6.89 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, { Component, Children, cloneElement } from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
var defaultProps = {
className: "react-accordion-with-header",
multipleOkay: false,
firstOpen: false,
style: {
boxShadow: "0 0 0 1px rgba(63,63,68,.05), 0 1px 3px 0 rgba(63,63,68,.15)",
borderRadius: 3
}
};
var AccordionWithHeader = function (_Component) {
_inherits(AccordionWithHeader, _Component);
function AccordionWithHeader() {
var _temp, _this, _ret;
_classCallCheck(this, AccordionWithHeader);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _Component.call.apply(_Component, [this].concat(args))), _this), _this.state = _extends({
panels: [],
active: []
}, _this.props), _this.onClickHeader = function (panelIndex) {
var active = void 0;
if (_this.state.active.includes(panelIndex)) {
active = _this.state.active.filter(function (item) {
return item !== panelIndex;
});
} else {
active = !_this.props.multipleOkay ? [] : _this.state.active;
active.push(_this.state.panels[panelIndex]);
active.sort();
}
_this.setState(function (prevState) {
return {
active: active,
multipleOkay: prevState.multipleOkay !== _this.props.multipleOkay ? _this.props.multipleOkay : prevState.multipleOkay
};
}, function () {
if (_this.props.actionCallback) {
var _this$props;
// pass array of panels and accordion state back to actionCallback props function
var panelData = _this.state.panels.map(function (panel) {
return {
panel: panel,
open: active.includes(panel)
};
});
(_this$props = _this.props).actionCallback.apply(_this$props, [panelData].concat(_this.state));
}
});
}, _this.checkExpanded = function (indexKey, activePanelOrPanelsProps) {
if (Array.isArray(activePanelOrPanelsProps)) {
//multipleOkay is true
return activePanelOrPanelsProps.some(function (panel) {
return panel === indexKey;
});
} else {
return indexKey === activePanelOrPanelsProps;
}
}, _temp), _possibleConstructorReturn(_this, _ret);
}
AccordionWithHeader.prototype.componentDidMount = function componentDidMount() {
var panels = void 0;
var _props = this.props,
children = _props.children,
active = _props.active,
firstOpen = _props.firstOpen;
if (!children) {
throw new Error("AccordionWithHeader must have children!");
}
panels = Children.map(children, function (child) {
return +child.key;
});
// define the number of AccordionNode "panels" to control
this.setState({ panels: panels });
// allow firstOpen prop, but prefer an "active" array
if (firstOpen) {
this.setState({ active: [0] });
}
// if this.props.active is defined, validate it is an array
// and that it is a valid instance of the panels array
if (typeof active !== "undefined") {
var validateActive = function validateActive() {
if (typeof active === "number" || !Array.isArray(active)) {
throw new Error("this.props.active must be an array");
}
active.forEach(function (active) {
if (!panels.includes(active)) {
throw new Error("Items in this.props.active array are not included in panel array!\n Check that one or more array indexes are properly passed in.");
}
});
};
validateActive();
this.setState(function (prevState) {
return {
active: Array.from(new Set([].concat(prevState.active, active)))
};
});
}
};
AccordionWithHeader.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
// Only needs to render if children have been dynamically aded/removed
if (prevProps.children.length != this.props.children.length) {
var children = this.props.children;
var panels = Children.map(children, function (child) {
return +child.key;
});
this.setState({ panels: panels });
}
};
AccordionWithHeader.prototype.render = function render() {
var _this2 = this;
var _props2 = this.props,
className = _props2.className,
style = _props2.style,
children = _props2.children,
active = _props2.active;
var internalControl = !active;
var panelsToCheck = internalControl ? this.state.active : active;
return React.createElement(
"div",
{ className: classNames(className), style: _extends({}, style) },
Children.map(children, function (item, index) {
// lets render the <AccordionNode /> and its kids
return cloneElement(item, {
indexKey: index, // needed for child ref if template prop is used
key: index,
onClickHeader: function onClickHeader() {
return _this2.onClickHeader(index);
},
isExpanded: _this2.checkExpanded(index, panelsToCheck)
});
})
);
};
return AccordionWithHeader;
}(Component);
export { AccordionWithHeader as default };
AccordionWithHeader.propTypes = process.env.NODE_ENV !== "production" ? {
className: PropTypes.string,
style: PropTypes.object,
firstOpen: PropTypes.bool,
multipleOkay: PropTypes.bool,
active: PropTypes.array,
actionCallback: PropTypes.func
} : {};
AccordionWithHeader.defaultProps = defaultProps;