semantic-ui-react
Version:
The official Semantic-UI-React integration.
215 lines (171 loc) • 7.5 kB
JavaScript
import _extends from 'babel-runtime/helpers/extends';
import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _createClass from 'babel-runtime/helpers/createClass';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
import _keys from 'lodash/keys';
import _omit from 'lodash/omit';
import _isFunction from 'lodash/isFunction';
import _each from 'lodash/each';
import _has from 'lodash/has';
import _without from 'lodash/without';
import _includes from 'lodash/includes';
import cx from 'classnames';
import React, { Children, cloneElement, PropTypes } from 'react';
import { AutoControlledComponent as Component, customPropTypes, getElementType, META, useKeyOnly } from '../../lib';
import AccordionContent from './AccordionContent';
import AccordionTitle from './AccordionTitle';
/**
* An accordion allows users to toggle the display of sections of content.
*/
var Accordion = function (_Component) {
_inherits(Accordion, _Component);
function Accordion() {
var _ref;
_classCallCheck(this, Accordion);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var _this = _possibleConstructorReturn(this, (_ref = Accordion.__proto__ || Object.getPrototypeOf(Accordion)).call.apply(_ref, [this].concat(args)));
_this.state = {};
_this.handleTitleClick = function (e, index) {
var _this$props = _this.props,
onTitleClick = _this$props.onTitleClick,
exclusive = _this$props.exclusive;
var activeIndex = _this.state.activeIndex;
var newIndex = void 0;
if (exclusive) {
newIndex = index === activeIndex ? -1 : index;
} else {
// check to see if index is in array, and remove it, if not then add it
newIndex = _includes(activeIndex, index) ? _without(activeIndex, index) : [].concat(_toConsumableArray(activeIndex), [index]);
}
_this.trySetState({ activeIndex: newIndex });
if (onTitleClick) onTitleClick(e, index);
};
_this.isIndexActive = function (index) {
var exclusive = _this.props.exclusive;
var activeIndex = _this.state.activeIndex;
return exclusive ? activeIndex === index : _includes(activeIndex, index);
};
_this.renderChildren = function () {
var children = _this.props.children;
var titleIndex = 0;
var contentIndex = 0;
return Children.map(children, function (child) {
var isTitle = child.type === AccordionTitle;
var isContent = child.type === AccordionContent;
if (isTitle) {
var currentIndex = titleIndex;
var isActive = _has(child, 'props.active') ? child.props.active : _this.isIndexActive(titleIndex);
var onClick = function onClick(e) {
_this.handleTitleClick(e, currentIndex);
if (child.props.onClick) child.props.onClick(e, currentIndex);
};
titleIndex++;
return cloneElement(child, _extends({}, child.props, { active: isActive, onClick: onClick }));
}
if (isContent) {
var _isActive = _has(child, 'props.active') ? child.props.active : _this.isIndexActive(contentIndex);
contentIndex++;
return cloneElement(child, _extends({}, child.props, { active: _isActive }));
}
return child;
});
};
_this.renderPanels = function () {
var panels = _this.props.panels;
var children = [];
_each(panels, function (panel, i) {
var isActive = _has(panel, 'active') ? panel.active : _this.isIndexActive(i);
var onClick = function onClick(e) {
_this.handleTitleClick(e, i);
if (panel.onClick) panel.onClick(e, i);
};
// implement all methods of creating a key that are supported in factories
var key = panel.key || _isFunction(panel.childKey) && panel.childKey(panel) || panel.childKey && panel.childKey || panel.title;
children.push(AccordionTitle.create({ active: isActive, onClick: onClick, key: key + '-title', content: panel.title }));
children.push(AccordionContent.create({ active: isActive, key: key + '-content', content: panel.content }));
});
return children;
};
_this.state = {
activeIndex: _this.props.exclusive ? -1 : [-1]
};
return _this;
}
_createClass(Accordion, [{
key: 'render',
value: function render() {
var _props = this.props,
className = _props.className,
fluid = _props.fluid,
inverted = _props.inverted,
panels = _props.panels,
styled = _props.styled;
var classes = cx('ui', useKeyOnly(fluid, 'fluid'), useKeyOnly(inverted, 'inverted'), useKeyOnly(styled, 'styled'), 'accordion', className);
var rest = _omit(this.props, _keys(Accordion.propTypes));
var ElementType = getElementType(Accordion, this.props);
return React.createElement(
ElementType,
_extends({}, rest, { className: classes }),
panels ? this.renderPanels() : this.renderChildren()
);
}
}]);
return Accordion;
}(Component);
Accordion.defaultProps = {
exclusive: true
};
Accordion.autoControlledProps = ['activeIndex'];
Accordion._meta = {
name: 'Accordion',
type: META.TYPES.MODULE
};
Accordion.Content = AccordionContent;
Accordion.Title = AccordionTitle;
export default Accordion;
process.env.NODE_ENV !== "production" ? Accordion.propTypes = {
/** An element type to render as (string or function). */
as: customPropTypes.as,
/** Index of the currently active panel. */
activeIndex: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]),
/** Primary content. */
children: PropTypes.node,
/** Additional classes. */
className: PropTypes.string,
/** Initial activeIndex value. */
defaultActiveIndex: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]),
/** Only allow one panel open at a time. */
exclusive: PropTypes.bool,
/** Format to take up the width of it's container. */
fluid: PropTypes.bool,
/** Format for dark backgrounds. */
inverted: PropTypes.bool,
/**
* Called when a panel title is clicked.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {number} index - The index of the clicked panel.
*/
onTitleClick: PropTypes.func,
/**
* Create simple accordion panels from an array of { text: <string>, content: <custom> } objects.
* Object can optionally define an `active` key to open/close the panel.
* Object can opitonally define a `key` key used for title and content nodes' keys.
* Mutually exclusive with children.
* TODO: AccordionPanel should be a sub-component
*/
panels: customPropTypes.every([customPropTypes.disallow(['children']), PropTypes.arrayOf(PropTypes.shape({
key: PropTypes.string,
active: PropTypes.bool,
title: customPropTypes.contentShorthand,
content: customPropTypes.contentShorthand,
onClick: PropTypes.func
}))]),
/** Adds some basic styling to accordion panels. */
styled: PropTypes.bool
} : void 0;
Accordion.handledProps = ['activeIndex', 'as', 'children', 'className', 'defaultActiveIndex', 'exclusive', 'fluid', 'inverted', 'onTitleClick', 'panels', 'styled'];