react-collapsible
Version:
React component to wrap content in Collapsible element with trigger to open and close.
281 lines (248 loc) • 10.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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; }; }();
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
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; }
var Collapsible = function (_Component) {
_inherits(Collapsible, _Component);
function Collapsible(props) {
_classCallCheck(this, Collapsible);
// Bind class methods
var _this = _possibleConstructorReturn(this, (Collapsible.__proto__ || Object.getPrototypeOf(Collapsible)).call(this, props));
_this.handleTriggerClick = _this.handleTriggerClick.bind(_this);
_this.handleTransitionEnd = _this.handleTransitionEnd.bind(_this);
_this.continueOpenCollapsible = _this.continueOpenCollapsible.bind(_this);
// Defaults the dropdown to be closed
if (props.open) {
_this.state = {
isClosed: false,
shouldSwitchAutoOnNextCycle: false,
height: 'auto',
transition: 'none',
hasBeenOpened: true,
overflow: props.overflowWhenOpen,
inTransition: false
};
} else {
_this.state = {
isClosed: true,
shouldSwitchAutoOnNextCycle: false,
height: 0,
transition: 'height ' + props.transitionTime + 'ms ' + props.easing,
hasBeenOpened: false,
overflow: 'hidden',
inTransition: false
};
}
return _this;
}
_createClass(Collapsible, [{
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
var _this2 = this;
if (this.state.shouldOpenOnNextCycle) {
this.continueOpenCollapsible();
}
if (prevState.height === 'auto' && this.state.shouldSwitchAutoOnNextCycle === true) {
window.setTimeout(function () {
// Set small timeout to ensure a true re-render
_this2.setState({
height: 0,
overflow: 'hidden',
isClosed: true,
shouldSwitchAutoOnNextCycle: false
});
}, 50);
}
// If there has been a change in the open prop (controlled by accordion)
if (prevProps.open !== this.props.open) {
if (this.props.open === true) {
this.openCollapsible();
this.props.onOpening();
} else {
this.closeCollapsible();
this.props.onClosing();
}
}
}
}, {
key: 'closeCollapsible',
value: function closeCollapsible() {
this.setState({
shouldSwitchAutoOnNextCycle: true,
height: this.refs.inner.offsetHeight,
transition: 'height ' + this.props.transitionTime + 'ms ' + this.props.easing,
inTransition: true
});
}
}, {
key: 'openCollapsible',
value: function openCollapsible() {
this.setState({
inTransition: true,
shouldOpenOnNextCycle: true
});
}
}, {
key: 'continueOpenCollapsible',
value: function continueOpenCollapsible() {
this.setState({
height: this.refs.inner.offsetHeight,
transition: 'height ' + this.props.transitionTime + 'ms ' + this.props.easing,
isClosed: false,
hasBeenOpened: true,
inTransition: true,
shouldOpenOnNextCycle: false
});
}
}, {
key: 'handleTriggerClick',
value: function handleTriggerClick(event) {
event.preventDefault();
if (this.props.triggerDisabled) {
return;
}
if (this.props.handleTriggerClick) {
this.props.handleTriggerClick(this.props.accordionPosition);
} else {
if (this.state.isClosed === true) {
this.openCollapsible();
this.props.onOpening();
} else {
this.closeCollapsible();
this.props.onClosing();
}
}
}
}, {
key: 'renderNonClickableTriggerElement',
value: function renderNonClickableTriggerElement() {
if (this.props.triggerSibling && typeof this.props.triggerSibling === 'string') {
return _react2.default.createElement(
'span',
{ className: this.props.classParentString + '__trigger-sibling' },
this.props.triggerSibling
);
} else if (this.props.triggerSibling) {
return _react2.default.createElement(this.props.triggerSibling, null);
}
return null;
}
}, {
key: 'handleTransitionEnd',
value: function handleTransitionEnd() {
// Switch to height auto to make the container responsive
if (!this.state.isClosed) {
this.setState({ height: 'auto', overflow: this.props.overflowWhenOpen, inTransition: false });
this.props.onOpen();
} else {
this.setState({ inTransition: false });
this.props.onClose();
}
}
}, {
key: 'render',
value: function render() {
var dropdownStyle = {
height: this.state.height,
WebkitTransition: this.state.transition,
msTransition: this.state.transition,
transition: this.state.transition,
overflow: this.state.overflow
};
var openClass = this.state.isClosed ? 'is-closed' : 'is-open';
var disabledClass = this.props.triggerDisabled ? 'is-disabled' : '';
//If user wants different text when tray is open
var trigger = this.state.isClosed === false && this.props.triggerWhenOpen !== undefined ? this.props.triggerWhenOpen : this.props.trigger;
// Don't render children until the first opening of the Collapsible if lazy rendering is enabled
var children = this.props.lazyRender && !this.state.hasBeenOpened && this.state.isClosed && !this.state.inTransition ? null : this.props.children;
// Construct CSS classes strings
var triggerClassString = this.props.classParentString + '__trigger ' + openClass + ' ' + disabledClass + ' ' + (this.state.isClosed ? this.props.triggerClassName : this.props.triggerOpenedClassName);
var parentClassString = this.props.classParentString + ' ' + (this.state.isClosed ? this.props.className : this.props.openedClassName);
var outerClassString = this.props.classParentString + '__contentOuter ' + this.props.contentOuterClassName;
var innerClassString = this.props.classParentString + '__contentInner ' + this.props.contentInnerClassName;
return _react2.default.createElement(
'div',
{ className: parentClassString.trim() },
_react2.default.createElement(
'span',
{
className: triggerClassString.trim(),
onClick: this.handleTriggerClick },
trigger
),
this.renderNonClickableTriggerElement(),
_react2.default.createElement(
'div',
{
className: outerClassString.trim(),
ref: 'outer',
style: dropdownStyle,
onTransitionEnd: this.handleTransitionEnd
},
_react2.default.createElement(
'div',
{
className: innerClassString.trim(),
ref: 'inner'
},
children
)
)
);
}
}]);
return Collapsible;
}(_react.Component);
Collapsible.propTypes = {
transitionTime: _propTypes2.default.number,
easing: _propTypes2.default.string,
open: _propTypes2.default.bool,
classParentString: _propTypes2.default.string,
openedClassName: _propTypes2.default.string,
triggerClassName: _propTypes2.default.string,
triggerOpenedClassName: _propTypes2.default.string,
contentOuterClassName: _propTypes2.default.string,
contentInnerClassName: _propTypes2.default.string,
accordionPosition: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]),
handleTriggerClick: _propTypes2.default.func,
onOpen: _propTypes2.default.func,
onClose: _propTypes2.default.func,
onOpening: _propTypes2.default.func,
onClosing: _propTypes2.default.func,
trigger: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.element]),
triggerWhenOpen: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.element]),
triggerDisabled: _propTypes2.default.bool,
lazyRender: _propTypes2.default.bool,
overflowWhenOpen: _propTypes2.default.oneOf(['hidden', 'visible', 'auto', 'scroll', 'inherit', 'initial', 'unset']),
triggerSibling: _propTypes2.default.oneOfType([_propTypes2.default.element, _propTypes2.default.func])
};
Collapsible.defaultProps = {
transitionTime: 400,
easing: 'linear',
open: false,
classParentString: 'Collapsible',
triggerDisabled: false,
lazyRender: false,
overflowWhenOpen: 'hidden',
openedClassName: '',
triggerClassName: '',
triggerOpenedClassName: '',
contentOuterClassName: '',
contentInnerClassName: '',
className: '',
triggerSibling: null,
onOpen: function onOpen() {},
onClose: function onClose() {},
onOpening: function onOpening() {},
onClosing: function onClosing() {}
};
exports.default = Collapsible;