UNPKG

17.1 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 _useEventCallback = _interopRequireDefault(require("@restart/hooks/useEventCallback"));
15
16var _useUpdateEffect = _interopRequireDefault(require("@restart/hooks/useUpdateEffect"));
17
18var _useCommittedRef = _interopRequireDefault(require("@restart/hooks/useCommittedRef"));
19
20var _useTimeout = _interopRequireDefault(require("@restart/hooks/useTimeout"));
21
22var _classnames = _interopRequireDefault(require("classnames"));
23
24var _transitionEnd = _interopRequireDefault(require("dom-helpers/transitionEnd"));
25
26var _Transition = _interopRequireDefault(require("react-transition-group/Transition"));
27
28var _propTypes = _interopRequireDefault(require("prop-types"));
29
30var _react = _interopRequireWildcard(require("react"));
31
32var _uncontrollable = require("uncontrollable");
33
34var _CarouselCaption = _interopRequireDefault(require("./CarouselCaption"));
35
36var _CarouselItem = _interopRequireDefault(require("./CarouselItem"));
37
38var _ElementChildren = require("./ElementChildren");
39
40var _SafeAnchor = _interopRequireDefault(require("./SafeAnchor"));
41
42var _ThemeProvider = require("./ThemeProvider");
43
44var _triggerBrowserReflow = _interopRequireDefault(require("./triggerBrowserReflow"));
45
46var SWIPE_THRESHOLD = 40;
47var propTypes = {
48 /**
49 * @default 'carousel'
50 */
51 bsPrefix: _propTypes.default.string,
52 as: _propTypes.default.elementType,
53
54 /**
55 * Enables animation on the Carousel as it transitions between slides.
56 */
57 slide: _propTypes.default.bool,
58
59 /** Cross fade slides instead of the default slide animation */
60 fade: _propTypes.default.bool,
61
62 /**
63 * Show the Carousel previous and next arrows for changing the current slide
64 */
65 controls: _propTypes.default.bool,
66
67 /**
68 * Show a set of slide position indicators
69 */
70 indicators: _propTypes.default.bool,
71
72 /**
73 * Controls the current visible slide
74 *
75 * @controllable onSelect
76 */
77 activeIndex: _propTypes.default.number,
78
79 /**
80 * Callback fired when the active item changes.
81 *
82 * ```js
83 * (eventKey: number, event: Object | null) => void
84 * ```
85 *
86 * @controllable activeIndex
87 */
88 onSelect: _propTypes.default.func,
89
90 /**
91 * Callback fired when a slide transition starts.
92 *
93 * ```js
94 * (eventKey: number, direction: 'left' | 'right') => void
95 */
96 onSlide: _propTypes.default.func,
97
98 /**
99 * Callback fired when a slide transition ends.
100 *
101 * ```js
102 * (eventKey: number, direction: 'left' | 'right') => void
103 */
104 onSlid: _propTypes.default.func,
105
106 /**
107 * The amount of time to delay between automatically cycling an item. If `null`, carousel will not automatically cycle.
108 */
109 interval: _propTypes.default.number,
110
111 /** Whether the carousel should react to keyboard events. */
112 keyboard: _propTypes.default.bool,
113
114 /**
115 * If set to `"hover"`, pauses the cycling of the carousel on `mouseenter` and resumes the cycling of the carousel on `mouseleave`. If set to `false`, hovering over the carousel won't pause it.
116 *
117 * On touch-enabled devices, when set to `"hover"`, cycling will pause on `touchend` (once the user finished interacting with the carousel) for two intervals, before automatically resuming. Note that this is in addition to the above mouse behavior.
118 */
119 pause: _propTypes.default.oneOf(['hover', false]),
120
121 /** Whether the carousel should cycle continuously or have hard stops. */
122 wrap: _propTypes.default.bool,
123
124 /**
125 * Whether the carousel should support left/right swipe interactions on touchscreen devices.
126 */
127 touch: _propTypes.default.bool,
128
129 /** Override the default button icon for the "previous" control */
130 prevIcon: _propTypes.default.node,
131
132 /**
133 * Label shown to screen readers only, can be used to show the previous element
134 * in the carousel.
135 * Set to null to deactivate.
136 */
137 prevLabel: _propTypes.default.string,
138
139 /** Override the default button icon for the "next" control */
140 nextIcon: _propTypes.default.node,
141
142 /**
143 * Label shown to screen readers only, can be used to show the next element
144 * in the carousel.
145 * Set to null to deactivate.
146 */
147 nextLabel: _propTypes.default.string
148};
149var defaultProps = {
150 slide: true,
151 fade: false,
152 controls: true,
153 indicators: true,
154 defaultActiveIndex: 0,
155 interval: 5000,
156 keyboard: true,
157 pause: 'hover',
158 wrap: true,
159 touch: true,
160 prevIcon: /*#__PURE__*/_react.default.createElement("span", {
161 "aria-hidden": "true",
162 className: "carousel-control-prev-icon"
163 }),
164 prevLabel: 'Previous',
165 nextIcon: /*#__PURE__*/_react.default.createElement("span", {
166 "aria-hidden": "true",
167 className: "carousel-control-next-icon"
168 }),
169 nextLabel: 'Next'
170};
171
172function isVisible(element) {
173 if (!element || !element.style || !element.parentNode || !element.parentNode.style) {
174 return false;
175 }
176
177 var elementStyle = getComputedStyle(element);
178 return elementStyle.display !== 'none' && elementStyle.visibility !== 'hidden' && getComputedStyle(element.parentNode).display !== 'none';
179}
180
181function CarouselFunc(uncontrolledProps, ref) {
182 var _useUncontrolled = (0, _uncontrollable.useUncontrolled)(uncontrolledProps, {
183 activeIndex: 'onSelect'
184 }),
185 _useUncontrolled$as = _useUncontrolled.as,
186 Component = _useUncontrolled$as === void 0 ? 'div' : _useUncontrolled$as,
187 bsPrefix = _useUncontrolled.bsPrefix,
188 slide = _useUncontrolled.slide,
189 fade = _useUncontrolled.fade,
190 controls = _useUncontrolled.controls,
191 indicators = _useUncontrolled.indicators,
192 activeIndex = _useUncontrolled.activeIndex,
193 onSelect = _useUncontrolled.onSelect,
194 onSlide = _useUncontrolled.onSlide,
195 onSlid = _useUncontrolled.onSlid,
196 interval = _useUncontrolled.interval,
197 keyboard = _useUncontrolled.keyboard,
198 onKeyDown = _useUncontrolled.onKeyDown,
199 pause = _useUncontrolled.pause,
200 onMouseOver = _useUncontrolled.onMouseOver,
201 onMouseOut = _useUncontrolled.onMouseOut,
202 wrap = _useUncontrolled.wrap,
203 touch = _useUncontrolled.touch,
204 onTouchStart = _useUncontrolled.onTouchStart,
205 onTouchMove = _useUncontrolled.onTouchMove,
206 onTouchEnd = _useUncontrolled.onTouchEnd,
207 prevIcon = _useUncontrolled.prevIcon,
208 prevLabel = _useUncontrolled.prevLabel,
209 nextIcon = _useUncontrolled.nextIcon,
210 nextLabel = _useUncontrolled.nextLabel,
211 className = _useUncontrolled.className,
212 children = _useUncontrolled.children,
213 props = (0, _objectWithoutPropertiesLoose2.default)(_useUncontrolled, ["as", "bsPrefix", "slide", "fade", "controls", "indicators", "activeIndex", "onSelect", "onSlide", "onSlid", "interval", "keyboard", "onKeyDown", "pause", "onMouseOver", "onMouseOut", "wrap", "touch", "onTouchStart", "onTouchMove", "onTouchEnd", "prevIcon", "prevLabel", "nextIcon", "nextLabel", "className", "children"]);
214
215 var prefix = (0, _ThemeProvider.useBootstrapPrefix)(bsPrefix, 'carousel');
216 var nextDirectionRef = (0, _react.useRef)(null);
217
218 var _useState = (0, _react.useState)('next'),
219 direction = _useState[0],
220 setDirection = _useState[1];
221
222 var _useState2 = (0, _react.useState)(false),
223 paused = _useState2[0],
224 setPaused = _useState2[1];
225
226 var _useState3 = (0, _react.useState)(false),
227 isSliding = _useState3[0],
228 setIsSliding = _useState3[1];
229
230 var _useState4 = (0, _react.useState)(activeIndex || 0),
231 renderedActiveIndex = _useState4[0],
232 setRenderedActiveIndex = _useState4[1];
233
234 if (!isSliding && activeIndex !== renderedActiveIndex) {
235 if (nextDirectionRef.current) {
236 setDirection(nextDirectionRef.current);
237 } else {
238 setDirection((activeIndex || 0) > renderedActiveIndex ? 'next' : 'prev');
239 }
240
241 if (slide) {
242 setIsSliding(true);
243 }
244
245 setRenderedActiveIndex(activeIndex || 0);
246 }
247
248 (0, _react.useEffect)(function () {
249 if (nextDirectionRef.current) {
250 nextDirectionRef.current = null;
251 }
252 });
253 var numChildren = 0;
254 var activeChildInterval; // Iterate to grab all of the children's interval values
255 // (and count them, too)
256
257 (0, _ElementChildren.forEach)(children, function (child, index) {
258 ++numChildren;
259
260 if (index === activeIndex) {
261 activeChildInterval = child.props.interval;
262 }
263 });
264 var activeChildIntervalRef = (0, _useCommittedRef.default)(activeChildInterval);
265 var prev = (0, _react.useCallback)(function (event) {
266 if (isSliding) {
267 return;
268 }
269
270 var nextActiveIndex = renderedActiveIndex - 1;
271
272 if (nextActiveIndex < 0) {
273 if (!wrap) {
274 return;
275 }
276
277 nextActiveIndex = numChildren - 1;
278 }
279
280 nextDirectionRef.current = 'prev';
281
282 if (onSelect) {
283 onSelect(nextActiveIndex, event);
284 }
285 }, [isSliding, renderedActiveIndex, onSelect, wrap, numChildren]); // This is used in the setInterval, so it should not invalidate.
286
287 var next = (0, _useEventCallback.default)(function (event) {
288 if (isSliding) {
289 return;
290 }
291
292 var nextActiveIndex = renderedActiveIndex + 1;
293
294 if (nextActiveIndex >= numChildren) {
295 if (!wrap) {
296 return;
297 }
298
299 nextActiveIndex = 0;
300 }
301
302 nextDirectionRef.current = 'next';
303
304 if (onSelect) {
305 onSelect(nextActiveIndex, event);
306 }
307 });
308 var elementRef = (0, _react.useRef)();
309 (0, _react.useImperativeHandle)(ref, function () {
310 return {
311 element: elementRef.current,
312 prev: prev,
313 next: next
314 };
315 }); // This is used in the setInterval, so it should not invalidate.
316
317 var nextWhenVisible = (0, _useEventCallback.default)(function () {
318 if (!document.hidden && isVisible(elementRef.current)) {
319 next();
320 }
321 });
322 var slideDirection = direction === 'next' ? 'left' : 'right';
323 (0, _useUpdateEffect.default)(function () {
324 if (slide) {
325 // These callbacks will be handled by the <Transition> callbacks.
326 return;
327 }
328
329 if (onSlide) {
330 onSlide(renderedActiveIndex, slideDirection);
331 }
332
333 if (onSlid) {
334 onSlid(renderedActiveIndex, slideDirection);
335 }
336 }, [renderedActiveIndex]);
337 var orderClassName = prefix + "-item-" + direction;
338 var directionalClassName = prefix + "-item-" + slideDirection;
339 var handleEnter = (0, _react.useCallback)(function (node) {
340 (0, _triggerBrowserReflow.default)(node);
341
342 if (onSlide) {
343 onSlide(renderedActiveIndex, slideDirection);
344 }
345 }, [onSlide, renderedActiveIndex, slideDirection]);
346 var handleEntered = (0, _react.useCallback)(function () {
347 setIsSliding(false);
348
349 if (onSlid) {
350 onSlid(renderedActiveIndex, slideDirection);
351 }
352 }, [onSlid, renderedActiveIndex, slideDirection]);
353 var handleKeyDown = (0, _react.useCallback)(function (event) {
354 if (keyboard && !/input|textarea/i.test(event.target.tagName)) {
355 switch (event.key) {
356 case 'ArrowLeft':
357 event.preventDefault();
358 prev(event);
359 return;
360
361 case 'ArrowRight':
362 event.preventDefault();
363 next(event);
364 return;
365
366 default:
367 }
368 }
369
370 if (onKeyDown) {
371 onKeyDown(event);
372 }
373 }, [keyboard, onKeyDown, prev, next]);
374 var handleMouseOver = (0, _react.useCallback)(function (event) {
375 if (pause === 'hover') {
376 setPaused(true);
377 }
378
379 if (onMouseOver) {
380 onMouseOver(event);
381 }
382 }, [pause, onMouseOver]);
383 var handleMouseOut = (0, _react.useCallback)(function (event) {
384 setPaused(false);
385
386 if (onMouseOut) {
387 onMouseOut(event);
388 }
389 }, [onMouseOut]);
390 var touchStartXRef = (0, _react.useRef)(0);
391 var touchDeltaXRef = (0, _react.useRef)(0);
392 var touchUnpauseTimeout = (0, _useTimeout.default)();
393 var handleTouchStart = (0, _react.useCallback)(function (event) {
394 touchStartXRef.current = event.touches[0].clientX;
395 touchDeltaXRef.current = 0;
396
397 if (pause === 'hover') {
398 setPaused(true);
399 }
400
401 if (onTouchStart) {
402 onTouchStart(event);
403 }
404 }, [pause, onTouchStart]);
405 var handleTouchMove = (0, _react.useCallback)(function (event) {
406 if (event.touches && event.touches.length > 1) {
407 touchDeltaXRef.current = 0;
408 } else {
409 touchDeltaXRef.current = event.touches[0].clientX - touchStartXRef.current;
410 }
411
412 if (onTouchMove) {
413 onTouchMove(event);
414 }
415 }, [onTouchMove]);
416 var handleTouchEnd = (0, _react.useCallback)(function (event) {
417 if (touch) {
418 var touchDeltaX = touchDeltaXRef.current;
419
420 if (Math.abs(touchDeltaX) > SWIPE_THRESHOLD) {
421 if (touchDeltaX > 0) {
422 prev(event);
423 } else {
424 next(event);
425 }
426 }
427 }
428
429 if (pause === 'hover') {
430 touchUnpauseTimeout.set(function () {
431 setPaused(false);
432 }, interval || undefined);
433 }
434
435 if (onTouchEnd) {
436 onTouchEnd(event);
437 }
438 }, [touch, pause, prev, next, touchUnpauseTimeout, interval, onTouchEnd]);
439 var shouldPlay = interval != null && !paused && !isSliding;
440 var intervalHandleRef = (0, _react.useRef)();
441 (0, _react.useEffect)(function () {
442 var _ref, _activeChildIntervalR;
443
444 if (!shouldPlay) {
445 return undefined;
446 }
447
448 intervalHandleRef.current = window.setInterval(document.visibilityState ? nextWhenVisible : next, (_ref = (_activeChildIntervalR = activeChildIntervalRef.current) != null ? _activeChildIntervalR : interval) != null ? _ref : undefined);
449 return function () {
450 if (intervalHandleRef.current !== null) {
451 clearInterval(intervalHandleRef.current);
452 }
453 };
454 }, [shouldPlay, next, activeChildIntervalRef, interval, nextWhenVisible]);
455 var indicatorOnClicks = (0, _react.useMemo)(function () {
456 return indicators && Array.from({
457 length: numChildren
458 }, function (_, index) {
459 return function (event) {
460 if (onSelect) {
461 onSelect(index, event);
462 }
463 };
464 });
465 }, [indicators, numChildren, onSelect]);
466 return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({
467 ref: elementRef
468 }, props, {
469 onKeyDown: handleKeyDown,
470 onMouseOver: handleMouseOver,
471 onMouseOut: handleMouseOut,
472 onTouchStart: handleTouchStart,
473 onTouchMove: handleTouchMove,
474 onTouchEnd: handleTouchEnd,
475 className: (0, _classnames.default)(className, prefix, slide && 'slide', fade && prefix + "-fade")
476 }), indicators && /*#__PURE__*/_react.default.createElement("ol", {
477 className: prefix + "-indicators"
478 }, (0, _ElementChildren.map)(children, function (_child, index) {
479 return /*#__PURE__*/_react.default.createElement("li", {
480 key: index,
481 className: index === renderedActiveIndex ? 'active' : undefined,
482 onClick: indicatorOnClicks ? indicatorOnClicks[index] : undefined
483 });
484 })), /*#__PURE__*/_react.default.createElement("div", {
485 className: prefix + "-inner"
486 }, (0, _ElementChildren.map)(children, function (child, index) {
487 var isActive = index === renderedActiveIndex;
488 return slide ? /*#__PURE__*/_react.default.createElement(_Transition.default, {
489 in: isActive,
490 onEnter: isActive ? handleEnter : undefined,
491 onEntered: isActive ? handleEntered : undefined,
492 addEndListener: _transitionEnd.default
493 }, function (status) {
494 return /*#__PURE__*/_react.default.cloneElement(child, {
495 className: (0, _classnames.default)(child.props.className, isActive && status !== 'entered' && orderClassName, (status === 'entered' || status === 'exiting') && 'active', (status === 'entering' || status === 'exiting') && directionalClassName)
496 });
497 }) : /*#__PURE__*/_react.default.cloneElement(child, {
498 className: (0, _classnames.default)(child.props.className, isActive && 'active')
499 });
500 })), controls && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (wrap || activeIndex !== 0) && /*#__PURE__*/_react.default.createElement(_SafeAnchor.default, {
501 className: prefix + "-control-prev",
502 onClick: prev
503 }, prevIcon, prevLabel && /*#__PURE__*/_react.default.createElement("span", {
504 className: "sr-only"
505 }, prevLabel)), (wrap || activeIndex !== numChildren - 1) && /*#__PURE__*/_react.default.createElement(_SafeAnchor.default, {
506 className: prefix + "-control-next",
507 onClick: next
508 }, nextIcon, nextLabel && /*#__PURE__*/_react.default.createElement("span", {
509 className: "sr-only"
510 }, nextLabel))));
511}
512
513var Carousel = /*#__PURE__*/_react.default.forwardRef(CarouselFunc);
514
515Carousel.displayName = 'Carousel';
516Carousel.propTypes = propTypes;
517Carousel.defaultProps = defaultProps;
518Carousel.Caption = _CarouselCaption.default;
519Carousel.Item = _CarouselItem.default;
520var _default = Carousel;
521exports.default = _default;
522module.exports = exports["default"];
\No newline at end of file