UNPKG

14.5 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7exports.__esModule = true;
8exports.default = void 0;
9
10var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
12var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
13
14var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose"));
15
16var _classnames = _interopRequireDefault(require("classnames"));
17
18var _style = _interopRequireDefault(require("dom-helpers/style"));
19
20var _transition = _interopRequireDefault(require("dom-helpers/transition"));
21
22var _react = _interopRequireWildcard(require("react"));
23
24var _uncontrollable = require("uncontrollable");
25
26var _CarouselCaption = _interopRequireDefault(require("./CarouselCaption"));
27
28var _CarouselItem = _interopRequireDefault(require("./CarouselItem"));
29
30var _SafeAnchor = _interopRequireDefault(require("./SafeAnchor"));
31
32var _ElementChildren = require("./utils/ElementChildren");
33
34var _triggerBrowserReflow = _interopRequireDefault(require("./utils/triggerBrowserReflow"));
35
36var _ThemeProvider = require("./ThemeProvider");
37
38var countChildren = function countChildren(c) {
39 return _react.default.Children.toArray(c).filter(_react.default.isValidElement).length;
40};
41
42var SWIPE_THRESHOLD = 40; // TODO: `slide` should be `animate`.
43
44var defaultProps = {
45 slide: true,
46 fade: false,
47 interval: 5000,
48 keyboard: true,
49 pauseOnHover: true,
50 wrap: true,
51 indicators: true,
52 controls: true,
53 activeIndex: 0,
54 prevIcon: _react.default.createElement("span", {
55 "aria-hidden": "true",
56 className: "carousel-control-prev-icon"
57 }),
58 prevLabel: 'Previous',
59 nextIcon: _react.default.createElement("span", {
60 "aria-hidden": "true",
61 className: "carousel-control-next-icon"
62 }),
63 nextLabel: 'Next',
64 touch: true
65};
66
67var Carousel =
68/*#__PURE__*/
69function (_React$Component) {
70 (0, _inheritsLoose2.default)(Carousel, _React$Component);
71
72 function Carousel() {
73 var _this;
74
75 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
76 args[_key] = arguments[_key];
77 }
78
79 _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
80 _this.state = {
81 prevClasses: '',
82 currentClasses: 'active',
83 touchStartX: 0
84 };
85 _this.isUnmounted = false;
86 _this.carousel = _react.default.createRef();
87
88 _this.handleTouchStart = function (e) {
89 _this.setState({
90 touchStartX: e.changedTouches[0].screenX
91 });
92 };
93
94 _this.handleTouchEnd = function (e) {
95 // If the swipe is under the threshold, don't do anything.
96 if (Math.abs(e.changedTouches[0].screenX - _this.state.touchStartX) < SWIPE_THRESHOLD) return;
97
98 if (e.changedTouches[0].screenX < _this.state.touchStartX) {
99 // Swiping left to navigate to next item.
100 _this.handleNext(e);
101 } else {
102 // Swiping right to navigate to previous item.
103 _this.handlePrev(e);
104 }
105 };
106
107 _this.handleSlideEnd = function () {
108 var pendingIndex = _this._pendingIndex;
109 _this._isSliding = false;
110 _this._pendingIndex = null;
111 if (pendingIndex != null) _this.to(pendingIndex);else _this.cycle();
112 };
113
114 _this.handleMouseOut = function () {
115 _this.cycle();
116 };
117
118 _this.handleMouseOver = function () {
119 if (_this.props.pauseOnHover) _this.pause();
120 };
121
122 _this.handleKeyDown = function (event) {
123 if (/input|textarea/i.test(event.target.tagName)) return;
124
125 switch (event.key) {
126 case 'ArrowLeft':
127 event.preventDefault();
128
129 _this.handlePrev(event);
130
131 break;
132
133 case 'ArrowRight':
134 event.preventDefault();
135
136 _this.handleNext(event);
137
138 break;
139
140 default:
141 break;
142 }
143 };
144
145 _this.handleNextWhenVisible = function () {
146 if (!_this.isUnmounted && !document.hidden && (0, _style.default)(_this.carousel.current, 'visibility') !== 'hidden') {
147 _this.handleNext();
148 }
149 };
150
151 _this.handleNext = function (e) {
152 if (_this._isSliding) return;
153 var _this$props = _this.props,
154 wrap = _this$props.wrap,
155 activeIndex = _this$props.activeIndex;
156 var index = activeIndex + 1;
157 var count = countChildren(_this.props.children);
158
159 if (index > count - 1) {
160 if (!wrap) return;
161 index = 0;
162 }
163
164 _this.select(index, e, 'next');
165 };
166
167 _this.handlePrev = function (e) {
168 if (_this._isSliding) return;
169 var _this$props2 = _this.props,
170 wrap = _this$props2.wrap,
171 activeIndex = _this$props2.activeIndex;
172 var index = activeIndex - 1;
173
174 if (index < 0) {
175 if (!wrap) return;
176 index = countChildren(_this.props.children) - 1;
177 }
178
179 _this.select(index, e, 'prev');
180 };
181
182 return _this;
183 }
184
185 var _proto = Carousel.prototype;
186
187 _proto.componentDidMount = function componentDidMount() {
188 this.cycle();
189 };
190
191 Carousel.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, _ref) {
192 var previousActiveIndex = _ref.activeIndex;
193
194 if (nextProps.activeIndex !== previousActiveIndex) {
195 var lastPossibleIndex = countChildren(nextProps.children) - 1;
196 var nextIndex = Math.max(0, Math.min(nextProps.activeIndex, lastPossibleIndex));
197 var direction;
198
199 if (nextIndex === 0 && previousActiveIndex >= lastPossibleIndex || previousActiveIndex <= nextIndex) {
200 direction = 'next';
201 } else {
202 direction = 'prev';
203 }
204
205 return {
206 direction: direction,
207 previousActiveIndex: previousActiveIndex,
208 activeIndex: nextIndex
209 };
210 }
211
212 return null;
213 };
214
215 _proto.componentDidUpdate = function componentDidUpdate(_, prevState) {
216 var _this2 = this;
217
218 var _this$props3 = this.props,
219 bsPrefix = _this$props3.bsPrefix,
220 slide = _this$props3.slide,
221 onSlideEnd = _this$props3.onSlideEnd;
222 if (!slide || this.state.activeIndex === prevState.activeIndex || this._isSliding) return;
223 var _this$state = this.state,
224 activeIndex = _this$state.activeIndex,
225 direction = _this$state.direction;
226 var orderClassName, directionalClassName;
227
228 if (direction === 'next') {
229 orderClassName = bsPrefix + "-item-next";
230 directionalClassName = bsPrefix + "-item-left";
231 } else if (direction === 'prev') {
232 orderClassName = bsPrefix + "-item-prev";
233 directionalClassName = bsPrefix + "-item-right";
234 }
235
236 this._isSliding = true;
237 this.pause(); // eslint-disable-next-line react/no-did-update-set-state
238
239 this.safeSetState({
240 prevClasses: 'active',
241 currentClasses: orderClassName
242 }, function () {
243 var items = _this2.carousel.current.children;
244 var nextElement = items[activeIndex];
245 (0, _triggerBrowserReflow.default)(nextElement);
246
247 _this2.safeSetState({
248 prevClasses: (0, _classnames.default)('active', directionalClassName),
249 currentClasses: (0, _classnames.default)(orderClassName, directionalClassName)
250 }, function () {
251 return _transition.default.end(nextElement, function () {
252 _this2.safeSetState({
253 prevClasses: '',
254 currentClasses: 'active'
255 }, _this2.handleSlideEnd);
256
257 if (onSlideEnd) {
258 onSlideEnd();
259 }
260 });
261 });
262 });
263 };
264
265 _proto.componentWillUnmount = function componentWillUnmount() {
266 clearTimeout(this.timeout);
267 this.isUnmounted = true;
268 };
269
270 _proto.safeSetState = function safeSetState(state, cb) {
271 var _this3 = this;
272
273 if (this.isUnmounted) return;
274 this.setState(state, function () {
275 return !_this3.isUnmounted && cb();
276 });
277 } // This might be a public API.
278 ;
279
280 _proto.pause = function pause() {
281 this._isPaused = true;
282 clearInterval(this._interval);
283 this._interval = null;
284 };
285
286 _proto.cycle = function cycle() {
287 this._isPaused = false;
288 clearInterval(this._interval);
289 this._interval = null;
290
291 if (this.props.interval && !this._isPaused) {
292 this._interval = setInterval(document.visibilityState ? this.handleNextWhenVisible : this.handleNext, this.props.interval);
293 }
294 };
295
296 _proto.to = function to(index, event) {
297 var children = this.props.children;
298
299 if (index < 0 || index > countChildren(children) - 1) {
300 return;
301 }
302
303 if (this._isSliding) {
304 this._pendingIndex = index;
305 return;
306 }
307
308 this.select(index, event);
309 };
310
311 _proto.select = function select(index, event, direction) {
312 var _this4 = this;
313
314 clearTimeout(this.selectThrottle);
315 if (event && event.persist) event.persist(); // The timeout throttles fast clicks, in order to give any pending state
316 // a chance to update and propagate back through props
317
318 this.selectThrottle = setTimeout(function () {
319 clearTimeout(_this4.timeout);
320 var _this4$props = _this4.props,
321 activeIndex = _this4$props.activeIndex,
322 onSelect = _this4$props.onSelect;
323 if (index === activeIndex || _this4._isSliding || _this4.isUnmounted) return;
324 onSelect(index, direction || (index < activeIndex ? 'prev' : 'next'), event);
325 }, 50);
326 };
327
328 _proto.renderControls = function renderControls(properties) {
329 var bsPrefix = this.props.bsPrefix;
330 var wrap = properties.wrap,
331 children = properties.children,
332 activeIndex = properties.activeIndex,
333 prevIcon = properties.prevIcon,
334 nextIcon = properties.nextIcon,
335 prevLabel = properties.prevLabel,
336 nextLabel = properties.nextLabel;
337 var count = countChildren(children);
338 return [(wrap || activeIndex !== 0) && _react.default.createElement(_SafeAnchor.default, {
339 key: "prev",
340 className: bsPrefix + "-control-prev",
341 onClick: this.handlePrev
342 }, prevIcon, prevLabel && _react.default.createElement("span", {
343 className: "sr-only"
344 }, prevLabel)), (wrap || activeIndex !== count - 1) && _react.default.createElement(_SafeAnchor.default, {
345 key: "next",
346 className: bsPrefix + "-control-next",
347 onClick: this.handleNext
348 }, nextIcon, nextLabel && _react.default.createElement("span", {
349 className: "sr-only"
350 }, nextLabel))];
351 };
352
353 _proto.renderIndicators = function renderIndicators(children, activeIndex) {
354 var _this5 = this;
355
356 var bsPrefix = this.props.bsPrefix;
357 var indicators = [];
358 (0, _ElementChildren.forEach)(children, function (child, index) {
359 indicators.push(_react.default.createElement("li", {
360 key: index,
361 className: index === activeIndex ? 'active' : null,
362 onClick: function onClick(e) {
363 return _this5.to(index, e);
364 }
365 }), // Force whitespace between indicator elements. Bootstrap requires
366 // this for correct spacing of elements.
367 ' ');
368 });
369 return _react.default.createElement("ol", {
370 className: bsPrefix + "-indicators"
371 }, indicators);
372 };
373
374 _proto.render = function render() {
375 var _this$props4 = this.props,
376 _this$props4$as = _this$props4.as,
377 Component = _this$props4$as === void 0 ? 'div' : _this$props4$as,
378 bsPrefix = _this$props4.bsPrefix,
379 slide = _this$props4.slide,
380 fade = _this$props4.fade,
381 indicators = _this$props4.indicators,
382 controls = _this$props4.controls,
383 wrap = _this$props4.wrap,
384 touch = _this$props4.touch,
385 prevIcon = _this$props4.prevIcon,
386 prevLabel = _this$props4.prevLabel,
387 nextIcon = _this$props4.nextIcon,
388 nextLabel = _this$props4.nextLabel,
389 className = _this$props4.className,
390 children = _this$props4.children,
391 keyboard = _this$props4.keyboard,
392 _5 = _this$props4.activeIndex,
393 _4 = _this$props4.pauseOnHover,
394 _3 = _this$props4.interval,
395 _2 = _this$props4.onSelect,
396 _1 = _this$props4.onSlideEnd,
397 props = (0, _objectWithoutPropertiesLoose2.default)(_this$props4, ["as", "bsPrefix", "slide", "fade", "indicators", "controls", "wrap", "touch", "prevIcon", "prevLabel", "nextIcon", "nextLabel", "className", "children", "keyboard", "activeIndex", "pauseOnHover", "interval", "onSelect", "onSlideEnd"]);
398 var _this$state2 = this.state,
399 activeIndex = _this$state2.activeIndex,
400 previousActiveIndex = _this$state2.previousActiveIndex,
401 prevClasses = _this$state2.prevClasses,
402 currentClasses = _this$state2.currentClasses;
403 return (// eslint-disable-next-line jsx-a11y/no-static-element-interactions
404 _react.default.createElement(Component, (0, _extends2.default)({
405 onTouchStart: touch ? this.handleTouchStart : undefined,
406 onTouchEnd: touch ? this.handleTouchEnd : undefined
407 }, props, {
408 className: (0, _classnames.default)(className, bsPrefix, slide && 'slide', fade && bsPrefix + "-fade"),
409 onKeyDown: keyboard ? this.handleKeyDown : undefined,
410 onMouseOver: this.handleMouseOver,
411 onMouseOut: this.handleMouseOut
412 }), indicators && this.renderIndicators(children, activeIndex), _react.default.createElement("div", {
413 className: bsPrefix + "-inner",
414 ref: this.carousel
415 }, (0, _ElementChildren.map)(children, function (child, index) {
416 var current = index === activeIndex;
417 var previous = index === previousActiveIndex;
418 return (0, _react.cloneElement)(child, {
419 className: (0, _classnames.default)(child.props.className, bsPrefix + "-item", current && currentClasses, previous && prevClasses)
420 });
421 })), controls && this.renderControls({
422 wrap: wrap,
423 children: children,
424 activeIndex: activeIndex,
425 prevIcon: prevIcon,
426 prevLabel: prevLabel,
427 nextIcon: nextIcon,
428 nextLabel: nextLabel
429 }))
430 );
431 };
432
433 return Carousel;
434}(_react.default.Component);
435
436Carousel.defaultProps = defaultProps;
437var DecoratedCarousel = (0, _ThemeProvider.createBootstrapComponent)((0, _uncontrollable.uncontrollable)(Carousel, {
438 activeIndex: 'onSelect'
439}), 'carousel');
440DecoratedCarousel.Caption = _CarouselCaption.default;
441DecoratedCarousel.Item = _CarouselItem.default;
442var _default = DecoratedCarousel;
443exports.default = _default;
444module.exports = exports["default"];
\No newline at end of file