Skip to content

Commit db633c8

Browse files
committed
fix: leave one state of CCarousel definition
1 parent 625c04b commit db633c8

File tree

3 files changed

+181
-309
lines changed

3 files changed

+181
-309
lines changed

src/CCarousel.js

+180-84
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,224 @@
1-
import React, {useState, useRef} from 'react';
1+
import React, {useState, useEffect, useRef} from 'react';
22
import PropTypes from 'prop-types';
3-
import CCarouselCustom from './CCarouselCustom';
3+
import classNames from 'classnames';
4+
import {mapToCssModules} from './Shared/helper.js';
45
import CCarouselItem from './CCarouselItem';
5-
import CCarouselControl from './CCarouselControl';
6-
import CCarouselIndicators from './CCarouselIndicators';
7-
import CCarouselCaption from './CCarouselCaption';
6+
7+
export const Context = React.createContext({});
88

99
//component - CoreUI / CCarousel
1010

1111
const CCarousel = props=>{
1212

1313
const {
14+
className,
15+
cssModule,
1416
//
15-
custom,
16-
autoPlay,
17-
indicators,
18-
controls,
19-
items,
20-
goToIndex
17+
innerRef,
18+
slide,
2119
} = props;
2220

23-
const [activeIndex, setActiveIndex] = useState(props.defaultOpen || false);
24-
25-
const fields = useRef({animating: false}).current;
21+
const [direction, setDirection] = useState("right");
22+
const [indicatorClicked, setIndicatorClicked] = useState(false);
2623

27-
if (!custom){
24+
const fields = useRef({lastProps: null}).current;
2825

29-
const onExiting = ()=>{
30-
fields.animating = true;
26+
const setInterval = (properties = props)=>{
27+
clearInterval();
28+
if (properties.interval) {
29+
fields.cycleInterval = window.setInterval(() => {
30+
properties.next();
31+
}, parseInt(properties.interval, 10));
3132
}
33+
}
34+
35+
const clearInterval = ()=>{
36+
window.clearInterval(fields.cycleInterval);
37+
}
3238

33-
const onExited = ()=>{
34-
fields.animating = false;
39+
const hoverStart = (...args)=>{
40+
if (props.pause === 'hover') {
41+
clearInterval();
42+
}
43+
if (props.mouseEnter) {
44+
props.mouseEnter(...args);
3545
}
46+
}
3647

37-
const next = ()=>{
38-
if (fields.animating) return;
39-
const nextIndex = activeIndex === props.items.length - 1 ? 0 : activeIndex + 1;
40-
setActiveIndex(nextIndex);
48+
const hoverEnd = (...args)=>{
49+
if (props.pause === 'hover') {
50+
setInterval();
51+
}
52+
if (props.mouseLeave) {
53+
props.mouseLeave(...args);
4154
}
55+
}
4256

43-
const previous = ()=>{
44-
if (fields.animating) return;
45-
const nextIndex = activeIndex === 0 ? props.items.length - 1 : activeIndex - 1;
46-
setActiveIndex(nextIndex);
57+
const handleKeyPress = (e)=>{
58+
if (props.keyboard) {
59+
if (e.keyCode === 37) {
60+
props.previous();
61+
} else if (e.keyCode === 39) {
62+
props.next();
63+
}
4764
}
65+
}
66+
67+
const renderItems = (carouselItems, className)=>{
68+
const {slide} = props;
69+
return (
70+
<div role="listbox" className={className}>
71+
{carouselItems.map((item, index) => {
72+
const isIn = (index === props.activeIndex);
73+
return React.cloneElement(item, {
74+
in: isIn,
75+
slide: slide,
76+
});
77+
})}
78+
</div>
79+
);
80+
}
81+
82+
const setState = (oVal, nVal, setF)=>{
83+
if (nVal===oVal)
84+
return;
85+
setF(nVal);
86+
}
4887

49-
const toIndex = newIndex=>{
50-
if (fields.animating) return;
51-
setActiveIndex(newIndex);
88+
//effect
89+
90+
useEffect(() => {
91+
if (props.ride === 'carousel') {
92+
setInterval();
93+
}
94+
document.addEventListener('keyup', handleKeyPress);
95+
fields._handleKeyPress = handleKeyPress;
96+
return function cleanup() {
97+
clearInterval();
98+
document.removeEventListener('keyup', fields._handleKeyPress);
99+
};
100+
},
101+
[]);
102+
103+
useEffect(() => {
104+
if (fields.wrappedOnClickFunc){
105+
fields.wrappedOnClickFunc();
106+
setIndicatorClicked(false);
107+
fields.wrappedOnClickFunc = null;
52108
}
109+
});
110+
111+
//render
112+
113+
setInterval(props);
114+
if (fields.lastProps){
115+
if (fields.lastProps.activeIndex + 1 === props.activeIndex) {
116+
setState(direction, "right", setDirection);
117+
} else if (fields.lastProps.activeIndex - 1 === props.activeIndex) {
118+
setState(direction, "left", setDirection);
119+
} else if (fields.lastProps.activeIndex > props.activeIndex) {
120+
setState(direction, indicatorClicked ? "left" : "right", setDirection);
121+
} else if (fields.lastProps.activeIndex !== props.activeIndex) {
122+
setState(direction, indicatorClicked ? "right" : "left", setDirection);
123+
}
124+
setState(indicatorClicked, false, setIndicatorClicked);
125+
}
53126

54-
//render
55-
56-
const slides = items.map((item) => {
57-
return (
58-
<CCarouselItem
59-
onExiting={onExiting}
60-
onExited={onExited}
61-
key={item.src}
62-
>
63-
<img className="d-block w-100" src={item.src} alt={item.altText} />
64-
<CCarouselCaption captionText={item.caption} captionHeader={item.header || item.caption} />
65-
</CCarouselItem>
66-
);
67-
});
127+
fields.lastProps = props;
68128

129+
const outerClasses = mapToCssModules(classNames(
130+
className,
131+
'carousel',
132+
slide && 'slide'
133+
), cssModule);
134+
const innerClasses = mapToCssModules(classNames(
135+
'carousel-inner'
136+
), cssModule);
137+
138+
const children = props.children.filter(child => child !== null && child !== undefined && typeof child !== 'boolean');
139+
140+
const slidesOnly = children.every(child => child.type === CCarouselItem);
141+
142+
if (slidesOnly) {
69143
return (
70-
<CCarouselCustom
71-
activeIndex={activeIndex}
72-
next={next}
73-
previous={previous}
74-
ride={autoPlay ? 'carousel' : undefined}
75-
{...props}
76-
>
77-
{indicators && <CCarouselIndicators
78-
items={items}
79-
activeIndex={props.activeIndex || activeIndex}
80-
onClickHandler={goToIndex || toIndex}
81-
/>}
82-
{slides}
83-
{controls && <CCarouselControl
84-
direction="prev"
85-
directionText="Previous"
86-
onClickHandler={props.previous || previous}
87-
/>}
88-
{controls && <CCarouselControl
89-
direction="next"
90-
directionText="Next"
91-
onClickHandler={props.next || next}
92-
/>}
93-
</CCarouselCustom>
144+
<Context.Provider value={{direction: direction}}>
145+
<div className={outerClasses} onMouseEnter={hoverStart} onMouseLeave={hoverEnd} ref={innerRef}>
146+
{renderItems(children, innerClasses)}
147+
</div>
148+
</Context.Provider>
94149
);
150+
}
95151

152+
if (children[0] instanceof Array) {
153+
const carouselItems = children[0];
154+
const controlLeft = children[1];
155+
const controlRight = children[2];
156+
157+
return (
158+
<Context.Provider value={{direction: direction}}>
159+
<div className={outerClasses} onMouseEnter={hoverStart} onMouseLeave={hoverEnd} ref={innerRef}>
160+
{renderItems(carouselItems, innerClasses)}
161+
{controlLeft}
162+
{controlRight}
163+
</div>
164+
</Context.Provider>
165+
);
96166
}
97167

168+
const indicators = children[0];
169+
const wrappedOnClick = (e) => {
170+
if (typeof indicators.props.onClickHandler === 'function') {
171+
//this.setState({ indicatorClicked: true }, () => indicators.props.onClickHandler(e));
172+
fields.wrappedOnClickFunc = ()=>indicators.props.onClickHandler(e);
173+
setIndicatorClicked(true);
174+
}
175+
};
176+
177+
const wrappedIndicators = React.cloneElement(indicators, { onClickHandler: wrappedOnClick });
178+
const carouselItems = children[1];
179+
const controlLeft = children[2];
180+
const controlRight = children[3];
181+
98182
return (
99-
<CCarouselCustom {...props}/>
183+
<Context.Provider value={{direction: direction}}>
184+
<div className={outerClasses} onMouseEnter={hoverStart} onMouseLeave={hoverEnd} ref={innerRef}>
185+
{wrappedIndicators}
186+
{renderItems(carouselItems, innerClasses)}
187+
{controlLeft}
188+
{controlRight}
189+
</div>
190+
</Context.Provider>
100191
);
101192

102193
}
103194

104195
CCarousel.propTypes = {
105-
...CCarouselCustom.propTypes,
106-
//
107-
custom: PropTypes.bool,
196+
cssModule: PropTypes.object,
197+
className: PropTypes.string,
198+
children: PropTypes.array,
108199
//
109200
innerRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func, PropTypes.string]),
110-
items: PropTypes.array,
111-
indicators: PropTypes.bool,
112-
defaultOpen: PropTypes.bool,
113-
controls: PropTypes.bool,
114-
autoPlay: PropTypes.bool,
115201
activeIndex: PropTypes.number,
116-
next: PropTypes.func,
117-
previous: PropTypes.func,
118-
goToIndex: PropTypes.func
202+
next: PropTypes.func.isRequired,
203+
previous: PropTypes.func.isRequired,
204+
keyboard: PropTypes.bool,
205+
pause: PropTypes.oneOf(['hover', false]),
206+
ride: PropTypes.oneOf(['carousel']),
207+
interval: PropTypes.oneOfType([
208+
PropTypes.number,
209+
PropTypes.string,
210+
PropTypes.bool,
211+
]),
212+
mouseEnter: PropTypes.func,
213+
mouseLeave: PropTypes.func,
214+
slide: PropTypes.bool
119215
};
120216

121217
CCarousel.defaultProps = {
122-
custom: true,
123-
controls: true,
124-
indicators: true,
125-
autoPlay: true,
218+
interval: 5000,
219+
pause: 'hover',
220+
keyboard: true,
221+
slide: true,
126222
};
127223

128224
export default CCarousel;

0 commit comments

Comments
 (0)