UNPKG

14.1 kBJavaScriptView Raw
1"use strict";
2"use client";
3
4var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
5exports.__esModule = true;
6exports.default = void 0;
7var _useEventCallback = _interopRequireDefault(require("@restart/hooks/useEventCallback"));
8var _useUpdateEffect = _interopRequireDefault(require("@restart/hooks/useUpdateEffect"));
9var _useCommittedRef = _interopRequireDefault(require("@restart/hooks/useCommittedRef"));
10var _useTimeout = _interopRequireDefault(require("@restart/hooks/useTimeout"));
11var _Anchor = _interopRequireDefault(require("@restart/ui/Anchor"));
12var _classnames = _interopRequireDefault(require("classnames"));
13var React = _interopRequireWildcard(require("react"));
14var _uncontrollable = require("uncontrollable");
15var _CarouselCaption = _interopRequireDefault(require("./CarouselCaption"));
16var _CarouselItem = _interopRequireDefault(require("./CarouselItem"));
17var _ElementChildren = require("./ElementChildren");
18var _ThemeProvider = require("./ThemeProvider");
19var _transitionEndListener = _interopRequireDefault(require("./transitionEndListener"));
20var _triggerBrowserReflow = _interopRequireDefault(require("./triggerBrowserReflow"));
21var _TransitionWrapper = _interopRequireDefault(require("./TransitionWrapper"));
22var _jsxRuntime = require("react/jsx-runtime");
23function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
24function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
25const SWIPE_THRESHOLD = 40;
26function isVisible(element) {
27 if (!element || !element.style || !element.parentNode || !element.parentNode.style) {
28 return false;
29 }
30 const elementStyle = getComputedStyle(element);
31 return elementStyle.display !== 'none' && elementStyle.visibility !== 'hidden' && getComputedStyle(element.parentNode).display !== 'none';
32}
33const Carousel = /*#__PURE__*/React.forwardRef(({
34 defaultActiveIndex = 0,
35 ...uncontrolledProps
36}, ref) => {
37 const {
38 // Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
39 as: Component = 'div',
40 bsPrefix,
41 slide = true,
42 fade = false,
43 controls = true,
44 indicators = true,
45 indicatorLabels = [],
46 activeIndex,
47 onSelect,
48 onSlide,
49 onSlid,
50 interval = 5000,
51 keyboard = true,
52 onKeyDown,
53 pause = 'hover',
54 onMouseOver,
55 onMouseOut,
56 wrap = true,
57 touch = true,
58 onTouchStart,
59 onTouchMove,
60 onTouchEnd,
61 prevIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
62 "aria-hidden": "true",
63 className: "carousel-control-prev-icon"
64 }),
65 prevLabel = 'Previous',
66 nextIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
67 "aria-hidden": "true",
68 className: "carousel-control-next-icon"
69 }),
70 nextLabel = 'Next',
71 variant,
72 className,
73 children,
74 ...props
75 } = (0, _uncontrollable.useUncontrolled)({
76 defaultActiveIndex,
77 ...uncontrolledProps
78 }, {
79 activeIndex: 'onSelect'
80 });
81 const prefix = (0, _ThemeProvider.useBootstrapPrefix)(bsPrefix, 'carousel');
82 const isRTL = (0, _ThemeProvider.useIsRTL)();
83 const nextDirectionRef = (0, React.useRef)(null);
84 const [direction, setDirection] = (0, React.useState)('next');
85 const [paused, setPaused] = (0, React.useState)(false);
86 const [isSliding, setIsSliding] = (0, React.useState)(false);
87 const [renderedActiveIndex, setRenderedActiveIndex] = (0, React.useState)(activeIndex || 0);
88 (0, React.useEffect)(() => {
89 if (!isSliding && activeIndex !== renderedActiveIndex) {
90 if (nextDirectionRef.current) {
91 setDirection(nextDirectionRef.current);
92 } else {
93 setDirection((activeIndex || 0) > renderedActiveIndex ? 'next' : 'prev');
94 }
95 if (slide) {
96 setIsSliding(true);
97 }
98 setRenderedActiveIndex(activeIndex || 0);
99 }
100 }, [activeIndex, isSliding, renderedActiveIndex, slide]);
101 (0, React.useEffect)(() => {
102 if (nextDirectionRef.current) {
103 nextDirectionRef.current = null;
104 }
105 });
106 let numChildren = 0;
107 let activeChildInterval;
108
109 // Iterate to grab all of the children's interval values
110 // (and count them, too)
111 (0, _ElementChildren.forEach)(children, (child, index) => {
112 ++numChildren;
113 if (index === activeIndex) {
114 activeChildInterval = child.props.interval;
115 }
116 });
117 const activeChildIntervalRef = (0, _useCommittedRef.default)(activeChildInterval);
118 const prev = (0, React.useCallback)(event => {
119 if (isSliding) {
120 return;
121 }
122 let nextActiveIndex = renderedActiveIndex - 1;
123 if (nextActiveIndex < 0) {
124 if (!wrap) {
125 return;
126 }
127 nextActiveIndex = numChildren - 1;
128 }
129 nextDirectionRef.current = 'prev';
130 onSelect == null ? void 0 : onSelect(nextActiveIndex, event);
131 }, [isSliding, renderedActiveIndex, onSelect, wrap, numChildren]);
132
133 // This is used in the setInterval, so it should not invalidate.
134 const next = (0, _useEventCallback.default)(event => {
135 if (isSliding) {
136 return;
137 }
138 let nextActiveIndex = renderedActiveIndex + 1;
139 if (nextActiveIndex >= numChildren) {
140 if (!wrap) {
141 return;
142 }
143 nextActiveIndex = 0;
144 }
145 nextDirectionRef.current = 'next';
146 onSelect == null ? void 0 : onSelect(nextActiveIndex, event);
147 });
148 const elementRef = (0, React.useRef)();
149 (0, React.useImperativeHandle)(ref, () => ({
150 element: elementRef.current,
151 prev,
152 next
153 }));
154
155 // This is used in the setInterval, so it should not invalidate.
156 const nextWhenVisible = (0, _useEventCallback.default)(() => {
157 if (!document.hidden && isVisible(elementRef.current)) {
158 if (isRTL) {
159 prev();
160 } else {
161 next();
162 }
163 }
164 });
165 const slideDirection = direction === 'next' ? 'start' : 'end';
166 (0, _useUpdateEffect.default)(() => {
167 if (slide) {
168 // These callbacks will be handled by the <Transition> callbacks.
169 return;
170 }
171 onSlide == null ? void 0 : onSlide(renderedActiveIndex, slideDirection);
172 onSlid == null ? void 0 : onSlid(renderedActiveIndex, slideDirection);
173 }, [renderedActiveIndex]);
174 const orderClassName = `${prefix}-item-${direction}`;
175 const directionalClassName = `${prefix}-item-${slideDirection}`;
176 const handleEnter = (0, React.useCallback)(node => {
177 (0, _triggerBrowserReflow.default)(node);
178 onSlide == null ? void 0 : onSlide(renderedActiveIndex, slideDirection);
179 }, [onSlide, renderedActiveIndex, slideDirection]);
180 const handleEntered = (0, React.useCallback)(() => {
181 setIsSliding(false);
182 onSlid == null ? void 0 : onSlid(renderedActiveIndex, slideDirection);
183 }, [onSlid, renderedActiveIndex, slideDirection]);
184 const handleKeyDown = (0, React.useCallback)(event => {
185 if (keyboard && !/input|textarea/i.test(event.target.tagName)) {
186 switch (event.key) {
187 case 'ArrowLeft':
188 event.preventDefault();
189 if (isRTL) {
190 next(event);
191 } else {
192 prev(event);
193 }
194 return;
195 case 'ArrowRight':
196 event.preventDefault();
197 if (isRTL) {
198 prev(event);
199 } else {
200 next(event);
201 }
202 return;
203 default:
204 }
205 }
206 onKeyDown == null ? void 0 : onKeyDown(event);
207 }, [keyboard, onKeyDown, prev, next, isRTL]);
208 const handleMouseOver = (0, React.useCallback)(event => {
209 if (pause === 'hover') {
210 setPaused(true);
211 }
212 onMouseOver == null ? void 0 : onMouseOver(event);
213 }, [pause, onMouseOver]);
214 const handleMouseOut = (0, React.useCallback)(event => {
215 setPaused(false);
216 onMouseOut == null ? void 0 : onMouseOut(event);
217 }, [onMouseOut]);
218 const touchStartXRef = (0, React.useRef)(0);
219 const touchDeltaXRef = (0, React.useRef)(0);
220 const touchUnpauseTimeout = (0, _useTimeout.default)();
221 const handleTouchStart = (0, React.useCallback)(event => {
222 touchStartXRef.current = event.touches[0].clientX;
223 touchDeltaXRef.current = 0;
224 if (pause === 'hover') {
225 setPaused(true);
226 }
227 onTouchStart == null ? void 0 : onTouchStart(event);
228 }, [pause, onTouchStart]);
229 const handleTouchMove = (0, React.useCallback)(event => {
230 if (event.touches && event.touches.length > 1) {
231 touchDeltaXRef.current = 0;
232 } else {
233 touchDeltaXRef.current = event.touches[0].clientX - touchStartXRef.current;
234 }
235 onTouchMove == null ? void 0 : onTouchMove(event);
236 }, [onTouchMove]);
237 const handleTouchEnd = (0, React.useCallback)(event => {
238 if (touch) {
239 const touchDeltaX = touchDeltaXRef.current;
240 if (Math.abs(touchDeltaX) > SWIPE_THRESHOLD) {
241 if (touchDeltaX > 0) {
242 prev(event);
243 } else {
244 next(event);
245 }
246 }
247 }
248 if (pause === 'hover') {
249 touchUnpauseTimeout.set(() => {
250 setPaused(false);
251 }, interval || undefined);
252 }
253 onTouchEnd == null ? void 0 : onTouchEnd(event);
254 }, [touch, pause, prev, next, touchUnpauseTimeout, interval, onTouchEnd]);
255 const shouldPlay = interval != null && !paused && !isSliding;
256 const intervalHandleRef = (0, React.useRef)();
257 (0, React.useEffect)(() => {
258 var _ref, _activeChildIntervalR;
259 if (!shouldPlay) {
260 return undefined;
261 }
262 const nextFunc = isRTL ? prev : next;
263 intervalHandleRef.current = window.setInterval(document.visibilityState ? nextWhenVisible : nextFunc, (_ref = (_activeChildIntervalR = activeChildIntervalRef.current) != null ? _activeChildIntervalR : interval) != null ? _ref : undefined);
264 return () => {
265 if (intervalHandleRef.current !== null) {
266 clearInterval(intervalHandleRef.current);
267 }
268 };
269 }, [shouldPlay, prev, next, activeChildIntervalRef, interval, nextWhenVisible, isRTL]);
270 const indicatorOnClicks = (0, React.useMemo)(() => indicators && Array.from({
271 length: numChildren
272 }, (_, index) => event => {
273 onSelect == null ? void 0 : onSelect(index, event);
274 }), [indicators, numChildren, onSelect]);
275 return /*#__PURE__*/(0, _jsxRuntime.jsxs)(Component, {
276 ref: elementRef,
277 ...props,
278 onKeyDown: handleKeyDown,
279 onMouseOver: handleMouseOver,
280 onMouseOut: handleMouseOut,
281 onTouchStart: handleTouchStart,
282 onTouchMove: handleTouchMove,
283 onTouchEnd: handleTouchEnd,
284 className: (0, _classnames.default)(className, prefix, slide && 'slide', fade && `${prefix}-fade`, variant && `${prefix}-${variant}`),
285 children: [indicators && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
286 className: `${prefix}-indicators`,
287 children: (0, _ElementChildren.map)(children, (_, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
288 type: "button",
289 "data-bs-target": "" // Bootstrap requires this in their css.
290 ,
291 "aria-label": indicatorLabels != null && indicatorLabels.length ? indicatorLabels[index] : `Slide ${index + 1}`,
292 className: index === renderedActiveIndex ? 'active' : undefined,
293 onClick: indicatorOnClicks ? indicatorOnClicks[index] : undefined,
294 "aria-current": index === renderedActiveIndex
295 }, index))
296 }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
297 className: `${prefix}-inner`,
298 children: (0, _ElementChildren.map)(children, (child, index) => {
299 const isActive = index === renderedActiveIndex;
300 return slide ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_TransitionWrapper.default, {
301 in: isActive,
302 onEnter: isActive ? handleEnter : undefined,
303 onEntered: isActive ? handleEntered : undefined,
304 addEndListener: _transitionEndListener.default,
305 children: (status, innerProps) => /*#__PURE__*/React.cloneElement(child, {
306 ...innerProps,
307 className: (0, _classnames.default)(child.props.className, isActive && status !== 'entered' && orderClassName, (status === 'entered' || status === 'exiting') && 'active', (status === 'entering' || status === 'exiting') && directionalClassName)
308 })
309 }) : /*#__PURE__*/React.cloneElement(child, {
310 className: (0, _classnames.default)(child.props.className, isActive && 'active')
311 });
312 })
313 }), controls && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
314 children: [(wrap || activeIndex !== 0) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Anchor.default, {
315 className: `${prefix}-control-prev`,
316 onClick: prev,
317 children: [prevIcon, prevLabel && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
318 className: "visually-hidden",
319 children: prevLabel
320 })]
321 }), (wrap || activeIndex !== numChildren - 1) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Anchor.default, {
322 className: `${prefix}-control-next`,
323 onClick: next,
324 children: [nextIcon, nextLabel && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
325 className: "visually-hidden",
326 children: nextLabel
327 })]
328 })]
329 })]
330 });
331});
332Carousel.displayName = 'Carousel';
333var _default = Object.assign(Carousel, {
334 Caption: _CarouselCaption.default,
335 Item: _CarouselItem.default
336});
337exports.default = _default;
338module.exports = exports.default;
\No newline at end of file