UNPKG

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