UNPKG

6.93 kBJavaScriptView Raw
1import _extends from 'babel-runtime/helpers/extends';
2import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
3import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
4import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
5import _inherits from 'babel-runtime/helpers/inherits';
6
7var _collapseStyles;
8
9import classNames from 'classnames';
10import css from 'dom-helpers/style';
11import React from 'react';
12import PropTypes from 'prop-types';
13import Transition, { EXITED, ENTERED, ENTERING, EXITING } from 'react-transition-group/Transition';
14
15import capitalize from './utils/capitalize';
16import createChainedFunction from './utils/createChainedFunction';
17
18var MARGINS = {
19 height: ['marginTop', 'marginBottom'],
20 width: ['marginLeft', 'marginRight']
21};
22
23// reading a dimension prop will cause the browser to recalculate,
24// which will let our animations work
25function triggerBrowserReflow(node) {
26 node.offsetHeight; // eslint-disable-line no-unused-expressions
27}
28
29function getDimensionValue(dimension, elem) {
30 var value = elem['offset' + capitalize(dimension)];
31 var margins = MARGINS[dimension];
32
33 return value + parseInt(css(elem, margins[0]), 10) + parseInt(css(elem, margins[1]), 10);
34}
35
36var collapseStyles = (_collapseStyles = {}, _collapseStyles[EXITED] = 'collapse', _collapseStyles[EXITING] = 'collapsing', _collapseStyles[ENTERING] = 'collapsing', _collapseStyles[ENTERED] = 'collapse in', _collapseStyles);
37
38var propTypes = {
39 /**
40 * Show the component; triggers the expand or collapse animation
41 */
42 in: PropTypes.bool,
43
44 /**
45 * Wait until the first "enter" transition to mount the component (add it to the DOM)
46 */
47 mountOnEnter: PropTypes.bool,
48
49 /**
50 * Unmount the component (remove it from the DOM) when it is collapsed
51 */
52 unmountOnExit: PropTypes.bool,
53
54 /**
55 * Run the expand animation when the component mounts, if it is initially
56 * shown
57 */
58 appear: PropTypes.bool,
59
60 /**
61 * Duration of the collapse animation in milliseconds, to ensure that
62 * finishing callbacks are fired even if the original browser transition end
63 * events are canceled
64 */
65 timeout: PropTypes.number,
66
67 /**
68 * Callback fired before the component expands
69 */
70 onEnter: PropTypes.func,
71 /**
72 * Callback fired after the component starts to expand
73 */
74 onEntering: PropTypes.func,
75 /**
76 * Callback fired after the component has expanded
77 */
78 onEntered: PropTypes.func,
79 /**
80 * Callback fired before the component collapses
81 */
82 onExit: PropTypes.func,
83 /**
84 * Callback fired after the component starts to collapse
85 */
86 onExiting: PropTypes.func,
87 /**
88 * Callback fired after the component has collapsed
89 */
90 onExited: PropTypes.func,
91
92 /**
93 * The dimension used when collapsing, or a function that returns the
94 * dimension
95 *
96 * _Note: Bootstrap only partially supports 'width'!
97 * You will need to supply your own CSS animation for the `.width` CSS class._
98 */
99 dimension: PropTypes.oneOfType([PropTypes.oneOf(['height', 'width']), PropTypes.func]),
100
101 /**
102 * Function that returns the height or width of the animating DOM node
103 *
104 * Allows for providing some custom logic for how much the Collapse component
105 * should animate in its specified dimension. Called with the current
106 * dimension prop value and the DOM node.
107 */
108 getDimensionValue: PropTypes.func,
109
110 /**
111 * ARIA role of collapsible element
112 */
113 role: PropTypes.string
114};
115
116var defaultProps = {
117 in: false,
118 timeout: 300,
119 mountOnEnter: false,
120 unmountOnExit: false,
121 appear: false,
122
123 dimension: 'height',
124 getDimensionValue: getDimensionValue
125};
126
127var Collapse = function (_React$Component) {
128 _inherits(Collapse, _React$Component);
129
130 function Collapse() {
131 var _temp, _this, _ret;
132
133 _classCallCheck(this, Collapse);
134
135 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
136 args[_key] = arguments[_key];
137 }
138
139 return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.handleEnter = function (elem) {
140 elem.style[_this.getDimension()] = '0';
141 }, _this.handleEntering = function (elem) {
142 var dimension = _this.getDimension();
143 elem.style[dimension] = _this._getScrollDimensionValue(elem, dimension);
144 }, _this.handleEntered = function (elem) {
145 elem.style[_this.getDimension()] = null;
146 }, _this.handleExit = function (elem) {
147 var dimension = _this.getDimension();
148 elem.style[dimension] = _this.props.getDimensionValue(dimension, elem) + 'px';
149 triggerBrowserReflow(elem);
150 }, _this.handleExiting = function (elem) {
151 elem.style[_this.getDimension()] = '0';
152 }, _temp), _possibleConstructorReturn(_this, _ret);
153 }
154
155 Collapse.prototype.getDimension = function getDimension() {
156 return typeof this.props.dimension === 'function' ? this.props.dimension() : this.props.dimension;
157 };
158
159 // for testing
160
161
162 Collapse.prototype._getScrollDimensionValue = function _getScrollDimensionValue(elem, dimension) {
163 return elem['scroll' + capitalize(dimension)] + 'px';
164 };
165
166 /* -- Expanding -- */
167
168
169 /* -- Collapsing -- */
170
171
172 Collapse.prototype.render = function render() {
173 var _this2 = this;
174
175 var _props = this.props,
176 onEnter = _props.onEnter,
177 onEntering = _props.onEntering,
178 onEntered = _props.onEntered,
179 onExit = _props.onExit,
180 onExiting = _props.onExiting,
181 className = _props.className,
182 children = _props.children,
183 props = _objectWithoutProperties(_props, ['onEnter', 'onEntering', 'onEntered', 'onExit', 'onExiting', 'className', 'children']);
184
185 delete props.dimension;
186 delete props.getDimensionValue;
187
188 var handleEnter = createChainedFunction(this.handleEnter, onEnter);
189 var handleEntering = createChainedFunction(this.handleEntering, onEntering);
190 var handleEntered = createChainedFunction(this.handleEntered, onEntered);
191 var handleExit = createChainedFunction(this.handleExit, onExit);
192 var handleExiting = createChainedFunction(this.handleExiting, onExiting);
193
194 return React.createElement(
195 Transition,
196 _extends({}, props, {
197 'aria-expanded': props.role ? props.in : null,
198 onEnter: handleEnter,
199 onEntering: handleEntering,
200 onEntered: handleEntered,
201 onExit: handleExit,
202 onExiting: handleExiting
203 }),
204 function (state, innerProps) {
205 return React.cloneElement(children, _extends({}, innerProps, {
206 className: classNames(className, children.props.className, collapseStyles[state], _this2.getDimension() === 'width' && 'width')
207 }));
208 }
209 );
210 };
211
212 return Collapse;
213}(React.Component);
214
215Collapse.propTypes = propTypes;
216Collapse.defaultProps = defaultProps;
217
218export default Collapse;
\No newline at end of file