UNPKG

14.1 kBJavaScriptView Raw
1'use strict';
2
3exports.__esModule = true;
4
5var _extends2 = require('babel-runtime/helpers/extends');
6
7var _extends3 = _interopRequireDefault(_extends2);
8
9var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
10
11var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
12
13var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
14
15var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
16
17var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
18
19var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
20
21var _inherits2 = require('babel-runtime/helpers/inherits');
22
23var _inherits3 = _interopRequireDefault(_inherits2);
24
25var _classnames = require('classnames');
26
27var _classnames2 = _interopRequireDefault(_classnames);
28
29var _react = require('react');
30
31var _react2 = _interopRequireDefault(_react);
32
33var _propTypes = require('prop-types');
34
35var _propTypes2 = _interopRequireDefault(_propTypes);
36
37var _CarouselCaption = require('./CarouselCaption');
38
39var _CarouselCaption2 = _interopRequireDefault(_CarouselCaption);
40
41var _CarouselItem = require('./CarouselItem');
42
43var _CarouselItem2 = _interopRequireDefault(_CarouselItem);
44
45var _Glyphicon = require('./Glyphicon');
46
47var _Glyphicon2 = _interopRequireDefault(_Glyphicon);
48
49var _SafeAnchor = require('./SafeAnchor');
50
51var _SafeAnchor2 = _interopRequireDefault(_SafeAnchor);
52
53var _bootstrapUtils = require('./utils/bootstrapUtils');
54
55var _ValidComponentChildren = require('./utils/ValidComponentChildren');
56
57var _ValidComponentChildren2 = _interopRequireDefault(_ValidComponentChildren);
58
59function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
60
61// TODO: `slide` should be `animate`.
62
63// TODO: Use uncontrollable.
64
65var propTypes = {
66 slide: _propTypes2.default.bool,
67 indicators: _propTypes2.default.bool,
68 /**
69 * The amount of time to delay between automatically cycling an item.
70 * If `null`, carousel will not automatically cycle.
71 */
72 interval: _propTypes2.default.number,
73 controls: _propTypes2.default.bool,
74 pauseOnHover: _propTypes2.default.bool,
75 wrap: _propTypes2.default.bool,
76 /**
77 * Callback fired when the active item changes.
78 *
79 * ```js
80 * (eventKey: any, ?event: Object) => any
81 * ```
82 *
83 * If this callback takes two or more arguments, the second argument will
84 * be a persisted event object with `direction` set to the direction of the
85 * transition.
86 */
87 onSelect: _propTypes2.default.func,
88 onSlideEnd: _propTypes2.default.func,
89 activeIndex: _propTypes2.default.number,
90 defaultActiveIndex: _propTypes2.default.number,
91 direction: _propTypes2.default.oneOf(['prev', 'next']),
92 prevIcon: _propTypes2.default.node,
93 /**
94 * Label shown to screen readers only, can be used to show the previous element
95 * in the carousel.
96 * Set to null to deactivate.
97 */
98 prevLabel: _propTypes2.default.string,
99 nextIcon: _propTypes2.default.node,
100 /**
101 * Label shown to screen readers only, can be used to show the next element
102 * in the carousel.
103 * Set to null to deactivate.
104 */
105 nextLabel: _propTypes2.default.string
106};
107
108var defaultProps = {
109 slide: true,
110 interval: 5000,
111 pauseOnHover: true,
112 wrap: true,
113 indicators: true,
114 controls: true,
115 prevIcon: _react2.default.createElement(_Glyphicon2.default, { glyph: 'chevron-left' }),
116 prevLabel: 'Previous',
117 nextIcon: _react2.default.createElement(_Glyphicon2.default, { glyph: 'chevron-right' }),
118 nextLabel: 'Next'
119};
120
121var Carousel = function (_React$Component) {
122 (0, _inherits3.default)(Carousel, _React$Component);
123
124 function Carousel(props, context) {
125 (0, _classCallCheck3.default)(this, Carousel);
126
127 var _this = (0, _possibleConstructorReturn3.default)(this, _React$Component.call(this, props, context));
128
129 _this.handleMouseOver = _this.handleMouseOver.bind(_this);
130 _this.handleMouseOut = _this.handleMouseOut.bind(_this);
131 _this.handlePrev = _this.handlePrev.bind(_this);
132 _this.handleNext = _this.handleNext.bind(_this);
133 _this.handleItemAnimateOutEnd = _this.handleItemAnimateOutEnd.bind(_this);
134
135 var defaultActiveIndex = props.defaultActiveIndex;
136
137
138 _this.state = {
139 activeIndex: defaultActiveIndex != null ? defaultActiveIndex : 0,
140 previousActiveIndex: null,
141 direction: null
142 };
143
144 _this.isUnmounted = false;
145 return _this;
146 }
147
148 Carousel.prototype.componentDidMount = function componentDidMount() {
149 this.waitForNext();
150 };
151
152 Carousel.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
153 var activeIndex = this.getActiveIndex();
154
155 if (nextProps.activeIndex != null && nextProps.activeIndex !== activeIndex) {
156 clearTimeout(this.timeout);
157
158 this.setState({
159 previousActiveIndex: activeIndex,
160 direction: nextProps.direction != null ? nextProps.direction : this.getDirection(activeIndex, nextProps.activeIndex)
161 });
162 }
163
164 if (nextProps.activeIndex == null && this.state.activeIndex >= nextProps.children.length) {
165 this.setState({
166 activeIndex: 0,
167 previousActiveIndex: null,
168 direction: null
169 });
170 }
171 };
172
173 Carousel.prototype.componentWillUnmount = function componentWillUnmount() {
174 clearTimeout(this.timeout);
175 this.isUnmounted = true;
176 };
177
178 Carousel.prototype.getActiveIndex = function getActiveIndex() {
179 var activeIndexProp = this.props.activeIndex;
180 return activeIndexProp != null ? activeIndexProp : this.state.activeIndex;
181 };
182
183 Carousel.prototype.getDirection = function getDirection(prevIndex, index) {
184 if (prevIndex === index) {
185 return null;
186 }
187
188 return prevIndex > index ? 'prev' : 'next';
189 };
190
191 Carousel.prototype.handleItemAnimateOutEnd = function handleItemAnimateOutEnd() {
192 var _this2 = this;
193
194 this.setState({
195 previousActiveIndex: null,
196 direction: null
197 }, function () {
198 _this2.waitForNext();
199
200 if (_this2.props.onSlideEnd) {
201 _this2.props.onSlideEnd();
202 }
203 });
204 };
205
206 Carousel.prototype.handleMouseOut = function handleMouseOut() {
207 if (this.isPaused) {
208 this.play();
209 }
210 };
211
212 Carousel.prototype.handleMouseOver = function handleMouseOver() {
213 if (this.props.pauseOnHover) {
214 this.pause();
215 }
216 };
217
218 Carousel.prototype.handleNext = function handleNext(e) {
219 var index = this.getActiveIndex() + 1;
220 var count = _ValidComponentChildren2.default.count(this.props.children);
221
222 if (index > count - 1) {
223 if (!this.props.wrap) {
224 return;
225 }
226 index = 0;
227 }
228
229 this.select(index, e, 'next');
230 };
231
232 Carousel.prototype.handlePrev = function handlePrev(e) {
233 var index = this.getActiveIndex() - 1;
234
235 if (index < 0) {
236 if (!this.props.wrap) {
237 return;
238 }
239 index = _ValidComponentChildren2.default.count(this.props.children) - 1;
240 }
241
242 this.select(index, e, 'prev');
243 };
244
245 // This might be a public API.
246
247
248 Carousel.prototype.pause = function pause() {
249 this.isPaused = true;
250 clearTimeout(this.timeout);
251 };
252
253 // This might be a public API.
254
255
256 Carousel.prototype.play = function play() {
257 this.isPaused = false;
258 this.waitForNext();
259 };
260
261 Carousel.prototype.select = function select(index, e, direction) {
262 clearTimeout(this.timeout);
263
264 // TODO: Is this necessary? Seems like the only risk is if the component
265 // unmounts while handleItemAnimateOutEnd fires.
266 if (this.isUnmounted) {
267 return;
268 }
269
270 var previousActiveIndex = this.props.slide ? this.getActiveIndex() : null;
271 direction = direction || this.getDirection(previousActiveIndex, index);
272
273 var onSelect = this.props.onSelect;
274
275
276 if (onSelect) {
277 if (onSelect.length > 1) {
278 // React SyntheticEvents are pooled, so we need to remove this event
279 // from the pool to add a custom property. To avoid unnecessarily
280 // removing objects from the pool, only do this when the listener
281 // actually wants the event.
282 if (e) {
283 e.persist();
284 e.direction = direction;
285 } else {
286 e = { direction: direction };
287 }
288
289 onSelect(index, e);
290 } else {
291 onSelect(index);
292 }
293 }
294
295 if (this.props.activeIndex == null && index !== previousActiveIndex) {
296 if (this.state.previousActiveIndex != null) {
297 // If currently animating don't activate the new index.
298 // TODO: look into queueing this canceled call and
299 // animating after the current animation has ended.
300 return;
301 }
302
303 this.setState({
304 activeIndex: index,
305 previousActiveIndex: previousActiveIndex,
306 direction: direction
307 });
308 }
309 };
310
311 Carousel.prototype.waitForNext = function waitForNext() {
312 var _props = this.props,
313 slide = _props.slide,
314 interval = _props.interval,
315 activeIndexProp = _props.activeIndex;
316
317
318 if (!this.isPaused && slide && interval && activeIndexProp == null) {
319 this.timeout = setTimeout(this.handleNext, interval);
320 }
321 };
322
323 Carousel.prototype.renderControls = function renderControls(properties) {
324 var wrap = properties.wrap,
325 children = properties.children,
326 activeIndex = properties.activeIndex,
327 prevIcon = properties.prevIcon,
328 nextIcon = properties.nextIcon,
329 bsProps = properties.bsProps,
330 prevLabel = properties.prevLabel,
331 nextLabel = properties.nextLabel;
332
333 var controlClassName = (0, _bootstrapUtils.prefix)(bsProps, 'control');
334 var count = _ValidComponentChildren2.default.count(children);
335
336 return [(wrap || activeIndex !== 0) && _react2.default.createElement(
337 _SafeAnchor2.default,
338 {
339 key: 'prev',
340 className: (0, _classnames2.default)(controlClassName, 'left'),
341 onClick: this.handlePrev
342 },
343 prevIcon,
344 prevLabel && _react2.default.createElement(
345 'span',
346 { className: 'sr-only' },
347 prevLabel
348 )
349 ), (wrap || activeIndex !== count - 1) && _react2.default.createElement(
350 _SafeAnchor2.default,
351 {
352 key: 'next',
353 className: (0, _classnames2.default)(controlClassName, 'right'),
354 onClick: this.handleNext
355 },
356 nextIcon,
357 nextLabel && _react2.default.createElement(
358 'span',
359 { className: 'sr-only' },
360 nextLabel
361 )
362 )];
363 };
364
365 Carousel.prototype.renderIndicators = function renderIndicators(children, activeIndex, bsProps) {
366 var _this3 = this;
367
368 var indicators = [];
369
370 _ValidComponentChildren2.default.forEach(children, function (child, index) {
371 indicators.push(_react2.default.createElement('li', {
372 key: index,
373 className: index === activeIndex ? 'active' : null,
374 onClick: function onClick(e) {
375 return _this3.select(index, e);
376 }
377 }),
378
379 // Force whitespace between indicator elements. Bootstrap requires
380 // this for correct spacing of elements.
381 ' ');
382 });
383
384 return _react2.default.createElement(
385 'ol',
386 { className: (0, _bootstrapUtils.prefix)(bsProps, 'indicators') },
387 indicators
388 );
389 };
390
391 Carousel.prototype.render = function render() {
392 var _this4 = this;
393
394 var _props2 = this.props,
395 slide = _props2.slide,
396 indicators = _props2.indicators,
397 controls = _props2.controls,
398 wrap = _props2.wrap,
399 prevIcon = _props2.prevIcon,
400 prevLabel = _props2.prevLabel,
401 nextIcon = _props2.nextIcon,
402 nextLabel = _props2.nextLabel,
403 className = _props2.className,
404 children = _props2.children,
405 props = (0, _objectWithoutProperties3.default)(_props2, ['slide', 'indicators', 'controls', 'wrap', 'prevIcon', 'prevLabel', 'nextIcon', 'nextLabel', 'className', 'children']);
406 var _state = this.state,
407 previousActiveIndex = _state.previousActiveIndex,
408 direction = _state.direction;
409
410 var _splitBsPropsAndOmit = (0, _bootstrapUtils.splitBsPropsAndOmit)(props, ['interval', 'pauseOnHover', 'onSelect', 'onSlideEnd', 'activeIndex', // Accessed via this.getActiveIndex().
411 'defaultActiveIndex', 'direction']),
412 bsProps = _splitBsPropsAndOmit[0],
413 elementProps = _splitBsPropsAndOmit[1];
414
415 var activeIndex = this.getActiveIndex();
416
417 var classes = (0, _extends3.default)({}, (0, _bootstrapUtils.getClassSet)(bsProps), {
418 slide: slide
419 });
420
421 return _react2.default.createElement(
422 'div',
423 (0, _extends3.default)({}, elementProps, {
424 className: (0, _classnames2.default)(className, classes),
425 onMouseOver: this.handleMouseOver,
426 onMouseOut: this.handleMouseOut
427 }),
428 indicators && this.renderIndicators(children, activeIndex, bsProps),
429 _react2.default.createElement(
430 'div',
431 { className: (0, _bootstrapUtils.prefix)(bsProps, 'inner') },
432 _ValidComponentChildren2.default.map(children, function (child, index) {
433 var active = index === activeIndex;
434 var previousActive = slide && index === previousActiveIndex;
435
436 return (0, _react.cloneElement)(child, {
437 active: active,
438 index: index,
439 animateOut: previousActive,
440 animateIn: active && previousActiveIndex != null && slide,
441 direction: direction,
442 onAnimateOutEnd: previousActive ? _this4.handleItemAnimateOutEnd : null
443 });
444 })
445 ),
446 controls && this.renderControls({
447 wrap: wrap,
448 children: children,
449 activeIndex: activeIndex,
450 prevIcon: prevIcon,
451 prevLabel: prevLabel,
452 nextIcon: nextIcon,
453 nextLabel: nextLabel,
454 bsProps: bsProps
455 })
456 );
457 };
458
459 return Carousel;
460}(_react2.default.Component);
461
462Carousel.propTypes = propTypes;
463Carousel.defaultProps = defaultProps;
464
465Carousel.Caption = _CarouselCaption2.default;
466Carousel.Item = _CarouselItem2.default;
467
468exports.default = (0, _bootstrapUtils.bsClass)('carousel', Carousel);
469module.exports = exports['default'];
\No newline at end of file