UNPKG

9.99 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7var _react = _interopRequireDefault(require("react"));
8var _propTypes = _interopRequireDefault(require("prop-types"));
9var _classnames = _interopRequireDefault(require("classnames"));
10var _CarouselItem = _interopRequireDefault(require("./CarouselItem"));
11var _CarouselContext = require("./CarouselContext");
12var _utils = require("./utils");
13function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
15const SWIPE_THRESHOLD = 40;
16const propTypes = {
17 /** the current active slide of the carousel */
18 activeIndex: _propTypes.default.number,
19 /** a function which should advance the carousel to the next slide (via activeIndex) */
20 next: _propTypes.default.func.isRequired,
21 /** a function which should advance the carousel to the previous slide (via activeIndex) */
22 previous: _propTypes.default.func.isRequired,
23 /** controls if the left and right arrow keys should control the carousel */
24 keyboard: _propTypes.default.bool,
25 /** If set to "hover", pauses the cycling of the carousel on mouseenter and resumes the cycling of the carousel on
26 * mouseleave. If set to false, hovering over the carousel won't pause it.
27 */
28 pause: _propTypes.default.oneOf(['hover', false]),
29 /** Autoplays the carousel after the user manually cycles the first item. If "carousel", autoplays the carousel on load. */
30 ride: _propTypes.default.oneOf(['carousel']),
31 /** the interval at which the carousel automatically cycles */
32 interval: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string, _propTypes.default.bool]),
33 children: _propTypes.default.array,
34 /** called when the mouse enters the Carousel */
35 mouseEnter: _propTypes.default.func,
36 /** called when the mouse exits the Carousel */
37 mouseLeave: _propTypes.default.func,
38 /** controls whether the slide animation on the Carousel works or not */
39 slide: _propTypes.default.bool,
40 /** make the controls, indicators and captions dark on the Carousel */
41 dark: _propTypes.default.bool,
42 fade: _propTypes.default.bool,
43 /** Change underlying component's CSS base class name */
44 cssModule: _propTypes.default.object,
45 /** Add custom class */
46 className: _propTypes.default.string,
47 /** Enable touch support */
48 enableTouch: _propTypes.default.bool
49};
50const propsToOmit = Object.keys(propTypes);
51const defaultProps = {
52 interval: 5000,
53 pause: 'hover',
54 keyboard: true,
55 slide: true,
56 enableTouch: true,
57 fade: false
58};
59class Carousel extends _react.default.Component {
60 constructor(props) {
61 super(props);
62 this.handleKeyPress = this.handleKeyPress.bind(this);
63 this.renderItems = this.renderItems.bind(this);
64 this.hoverStart = this.hoverStart.bind(this);
65 this.hoverEnd = this.hoverEnd.bind(this);
66 this.handleTouchStart = this.handleTouchStart.bind(this);
67 this.handleTouchEnd = this.handleTouchEnd.bind(this);
68 this.touchStartX = 0;
69 this.touchStartY = 0;
70 this.state = {
71 activeIndex: this.props.activeIndex,
72 direction: 'end',
73 indicatorClicked: false
74 };
75 }
76 componentDidMount() {
77 // Set up the cycle
78 if (this.props.ride === 'carousel') {
79 this.setInterval();
80 }
81
82 // TODO: move this to the specific carousel like bootstrap. Currently it will trigger ALL carousels on the page.
83 document.addEventListener('keyup', this.handleKeyPress);
84 }
85 static getDerivedStateFromProps(nextProps, prevState) {
86 let newState = null;
87 let {
88 activeIndex,
89 direction,
90 indicatorClicked
91 } = prevState;
92 if (nextProps.activeIndex !== activeIndex) {
93 // Calculate the direction to turn
94 if (nextProps.activeIndex === activeIndex + 1) {
95 direction = 'end';
96 } else if (nextProps.activeIndex === activeIndex - 1) {
97 direction = 'start';
98 } else if (nextProps.activeIndex < activeIndex) {
99 direction = indicatorClicked ? 'start' : 'end';
100 } else if (nextProps.activeIndex !== activeIndex) {
101 direction = indicatorClicked ? 'end' : 'start';
102 }
103 newState = {
104 activeIndex: nextProps.activeIndex,
105 direction,
106 indicatorClicked: false
107 };
108 }
109 return newState;
110 }
111 componentDidUpdate(prevProps, prevState) {
112 if (prevState.activeIndex === this.state.activeIndex) return;
113 this.setInterval();
114 }
115 componentWillUnmount() {
116 this.clearInterval();
117 document.removeEventListener('keyup', this.handleKeyPress);
118 }
119 handleKeyPress(evt) {
120 if (this.props.keyboard) {
121 if (evt.keyCode === 37) {
122 this.props.previous();
123 } else if (evt.keyCode === 39) {
124 this.props.next();
125 }
126 }
127 }
128 handleTouchStart(e) {
129 if (!this.props.enableTouch) {
130 return;
131 }
132 this.touchStartX = e.changedTouches[0].screenX;
133 this.touchStartY = e.changedTouches[0].screenY;
134 }
135 handleTouchEnd(e) {
136 if (!this.props.enableTouch) {
137 return;
138 }
139 const currentX = e.changedTouches[0].screenX;
140 const currentY = e.changedTouches[0].screenY;
141 const diffX = Math.abs(this.touchStartX - currentX);
142 const diffY = Math.abs(this.touchStartY - currentY);
143
144 // Don't swipe if Y-movement is bigger than X-movement
145 if (diffX < diffY) {
146 return;
147 }
148 if (diffX < SWIPE_THRESHOLD) {
149 return;
150 }
151 if (currentX < this.touchStartX) {
152 this.props.next();
153 } else {
154 this.props.previous();
155 }
156 }
157 getContextValue() {
158 return {
159 direction: this.state.direction
160 };
161 }
162 setInterval() {
163 // make sure not to have multiple intervals going...
164 this.clearInterval();
165 if (this.props.interval) {
166 this.cycleInterval = setInterval(() => {
167 this.props.next();
168 }, parseInt(this.props.interval, 10));
169 }
170 }
171 clearInterval() {
172 clearInterval(this.cycleInterval);
173 }
174 hoverStart(...args) {
175 if (this.props.pause === 'hover') {
176 this.clearInterval();
177 }
178 if (this.props.mouseEnter) {
179 this.props.mouseEnter(...args);
180 }
181 }
182 hoverEnd(...args) {
183 if (this.props.pause === 'hover') {
184 this.setInterval();
185 }
186 if (this.props.mouseLeave) {
187 this.props.mouseLeave(...args);
188 }
189 }
190 renderItems(carouselItems, className) {
191 const {
192 slide
193 } = this.props;
194 return /*#__PURE__*/_react.default.createElement("div", {
195 className: className
196 }, carouselItems.map((item, index) => {
197 const isIn = index === this.state.activeIndex;
198 return /*#__PURE__*/_react.default.cloneElement(item, {
199 in: isIn,
200 slide: slide
201 });
202 }));
203 }
204 render() {
205 const {
206 cssModule,
207 slide,
208 className,
209 dark,
210 fade
211 } = this.props;
212 const attributes = (0, _utils.omit)(this.props, propsToOmit);
213 const outerClasses = (0, _utils.mapToCssModules)((0, _classnames.default)(className, 'carousel', fade && 'carousel-fade', slide && 'slide', dark && 'carousel-dark'), cssModule);
214 const innerClasses = (0, _utils.mapToCssModules)((0, _classnames.default)('carousel-inner'), cssModule);
215
216 // filter out booleans, null, or undefined
217 const children = this.props.children.filter(child => child !== null && child !== undefined && typeof child !== 'boolean');
218 const slidesOnly = children.every(child => child.type === _CarouselItem.default);
219
220 // Rendering only slides
221 if (slidesOnly) {
222 return /*#__PURE__*/_react.default.createElement("div", _extends({}, attributes, {
223 className: outerClasses,
224 onMouseEnter: this.hoverStart,
225 onMouseLeave: this.hoverEnd
226 }), /*#__PURE__*/_react.default.createElement(_CarouselContext.CarouselContext.Provider, {
227 value: this.getContextValue()
228 }, this.renderItems(children, innerClasses)));
229 }
230
231 // Rendering slides and controls
232 if (children[0] instanceof Array) {
233 const carouselItems = children[0];
234 const controlLeft = children[1];
235 const controlRight = children[2];
236 return /*#__PURE__*/_react.default.createElement("div", _extends({}, attributes, {
237 className: outerClasses,
238 onMouseEnter: this.hoverStart,
239 onMouseLeave: this.hoverEnd
240 }), /*#__PURE__*/_react.default.createElement(_CarouselContext.CarouselContext.Provider, {
241 value: this.getContextValue()
242 }, this.renderItems(carouselItems, innerClasses), controlLeft, controlRight));
243 }
244
245 // Rendering indicators, slides and controls
246 const indicators = children[0];
247 const wrappedOnClick = e => {
248 if (typeof indicators.props.onClickHandler === 'function') {
249 this.setState({
250 indicatorClicked: true
251 }, () => indicators.props.onClickHandler(e));
252 }
253 };
254 const wrappedIndicators = /*#__PURE__*/_react.default.cloneElement(indicators, {
255 onClickHandler: wrappedOnClick
256 });
257 const carouselItems = children[1];
258 const controlLeft = children[2];
259 const controlRight = children[3];
260 return /*#__PURE__*/_react.default.createElement("div", _extends({}, attributes, {
261 className: outerClasses,
262 onMouseEnter: this.hoverStart,
263 onMouseLeave: this.hoverEnd,
264 onTouchStart: this.handleTouchStart,
265 onTouchEnd: this.handleTouchEnd
266 }), /*#__PURE__*/_react.default.createElement(_CarouselContext.CarouselContext.Provider, {
267 value: this.getContextValue()
268 }, wrappedIndicators, this.renderItems(carouselItems, innerClasses), controlLeft, controlRight));
269 }
270}
271Carousel.propTypes = propTypes;
272Carousel.defaultProps = defaultProps;
273var _default = Carousel;
274exports.default = _default;
\No newline at end of file