Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vertical render added and autoplay option added #266

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 131 additions & 27 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import mergeExtraData from './merge-extradata';

const isAndroidRTL = I18nManager.isRTL && Platform.OS === 'android';

type OrientationType = "horizontal" | "vertical"

const Orientations = {
horizontal: "horizontal",
vertical: "vertical"
}
poojan010 marked this conversation as resolved.
Show resolved Hide resolved

type Props<ItemT> = {
data: ItemT[];
renderItem: (
Expand Down Expand Up @@ -46,6 +53,8 @@ type Props<ItemT> = {
showPrevButton: boolean;
showSkipButton: boolean;
bottomButton: boolean;
orientation: OrientationType;
autoPlay: boolean,
} & FlatListProps<ItemT>;

type State = {
Expand Down Expand Up @@ -75,24 +84,51 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
showPrevButton: false,
showSkipButton: false,
bottomButton: false,
orientation: Orientations.horizontal,
autoPlay: false
};
state = {
width: 0,
height: 0,
activeIndex: 0,
};
flatList: FlatList<ItemT> | undefined;
activeInterval: any;

componentDidMount() {
if (this.props.autoPlay) this.startScroll();
}

// Clear interval when user closes
componentWillUnmount() {
clearInterval(this.activeInterval);
}

startScroll() {
this.activeInterval = setInterval(this.scroll, 2000);
}

scroll = () => {
if (this.state.activeIndex < this.props.data.length - 1) {
this.goToSlide(this.state.activeIndex + 1)
} else {
clearInterval(this.activeInterval);
}
}

goToSlide = (pageNum: number, triggerOnSlideChange?: boolean) => {
const sizeOfSlide = this.props.orientation === Orientations.horizontal
? this.state.width
: this.state.height
const prevNum = this.state.activeIndex;
this.setState({activeIndex: pageNum});
this.setState({ activeIndex: pageNum });
this.flatList?.scrollToOffset({
offset: this._rtlSafeIndex(pageNum) * this.state.width,
offset: this._rtlSafeIndex(pageNum) * sizeOfSlide,
});
if (triggerOnSlideChange && this.props.onSlideChange) {
this.props.onSlideChange(pageNum, prevNum);
}
};
}

// Get the list ref
getListRef = () => this.flatList;
Expand Down Expand Up @@ -141,15 +177,22 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
name: string,
onPress?: (e: GestureResponderEvent) => void,
) => {
const leftButtonStyle = this.props.orientation === Orientations.vertical
? styles.topButtonContainer
: styles.leftButtonContainer
const rightButtonStyle = this.props.orientation === Orientations.vertical
? styles.bottomButtonContainer
: styles.rightButtonContainer
const style =
name === 'Skip' || name === 'Prev'
? styles.leftButtonContainer
: styles.rightButtonContainer;
? leftButtonStyle
: rightButtonStyle;
return (
<View style={!this.props.bottomButton && style}>
<TouchableOpacity
onPress={onPress}
style={this.props.bottomButton && styles.flexOne}>
style={this.props.bottomButton && styles.flexOne}
>
{content}
</TouchableOpacity>
</View>
Expand Down Expand Up @@ -177,10 +220,10 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
_renderDoneButton = () =>
this.props.showDoneButton &&
this._renderButton(
'Done',
this.props.doneLabel,
this.props.onDone,
this.props.renderDoneButton,
'Done',
this.props.doneLabel,
this.props.onDone,
this.props.renderDoneButton,
);

_renderSkipButton = () =>
Expand All @@ -206,18 +249,30 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
const primaryButton = isLastSlide
? this._renderDoneButton()
: this._renderNextButton();
const containerStyle = this.props.orientation === Orientations.vertical
? styles.paginationContainerV
: styles.paginationContainerH
const paginationSafeArea = this.props.orientation === Orientations.vertical
? styles.paginationSafeAreaV
: styles.paginationSafeAreaH
const dotsContainerStyle = this.props.orientation === Orientations.vertical
? styles.paginationDotsV
: styles.paginationDotsH
const dotStyle = this.props.orientation === Orientations.vertical
? styles.dotV
: styles.dotH

return (
<View style={styles.paginationContainer}>
<SafeAreaView>
<View style={styles.paginationDots}>
<View style={containerStyle}>
<SafeAreaView style={paginationSafeArea}>
<View style={dotsContainerStyle}>
{this.props.data.length > 1 &&
this.props.data.map((_, i) =>
this.props.dotClickEnabled ? (
<TouchableOpacity
key={i}
style={[
styles.dot,
dotStyle,
this._rtlSafeIndex(i) === this.state.activeIndex
? this.props.activeDotStyle
: this.props.dotStyle,
Expand All @@ -228,7 +283,7 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
<View
key={i}
style={[
styles.dot,
dotStyle,
this._rtlSafeIndex(i) === this.state.activeIndex
? this.props.activeDotStyle
: this.props.dotStyle,
Expand All @@ -244,21 +299,27 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
);
};

_onMomentumScrollEnd = (e: {nativeEvent: NativeScrollEvent}) => {
const offset = e.nativeEvent.contentOffset.x;
_onMomentumScrollEnd = (e: { nativeEvent: NativeScrollEvent }) => {
const offset = this.props.orientation === "horizontal"
? e.nativeEvent.contentOffset.x
: e.nativeEvent.contentOffset.y;
const total = this.props.orientation === "horizontal"
? this.state.width
: this.state.height

// Touching very very quickly and continuous brings about
// a variation close to - but not quite - the width.
// That's why we round the number.
// Also, Android phones and their weird numbers
const newIndex = this._rtlSafeIndex(Math.round(offset / this.state.width));
const newIndex = this._rtlSafeIndex(Math.round(offset / total));
if (newIndex === this.state.activeIndex) {
// No page change, don't do anything
return;
}
const lastIndex = this.state.activeIndex;
this.setState({activeIndex: newIndex});
this.setState({ activeIndex: newIndex });
this.props.onSlideChange && this.props.onSlideChange(newIndex, lastIndex);
};
}

_onLayout = ({nativeEvent}: LayoutChangeEvent) => {
const {width, height} = nativeEvent.layout;
Expand All @@ -268,7 +329,7 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
// Set new scroll position
const func = () => {
this.flatList?.scrollToOffset({
offset: this._rtlSafeIndex(this.state.activeIndex) * width,
offset: this._rtlSafeIndex(this.state.activeIndex) * (this.props.orientation === Orientations.vertical ? height : width),
animated: false,
});
};
Expand All @@ -290,23 +351,26 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
renderItem,
data,
extraData,
orientation,
...otherProps
} = this.props;
/* eslint-enable @typescript-eslint/no-unused-vars */

// Merge component width and user-defined extraData
const extra = mergeExtraData(extraData, this.state.width);

const flatListStyle = orientation === Orientations.vertical ? styles.flatListV : styles.flatListH

return (
<View style={styles.flexOne}>
<FlatList
ref={(ref) => (this.flatList = ref as FlatList<ItemT>)}
data={this.props.data}
horizontal
horizontal={orientation === Orientations.horizontal}
pagingEnabled
showsHorizontalScrollIndicator={false}
bounces={false}
style={styles.flatList}
style={flatListStyle}
renderItem={this._renderItem}
onMomentumScrollEnd={this._onMomentumScrollEnd}
extraData={extra}
Expand All @@ -327,30 +391,62 @@ const styles = StyleSheet.create({
flexOne: {
flex: 1,
},
flatList: {
flatListH: {
flex: 1,
flexDirection: isAndroidRTL ? 'row-reverse' : 'row',
},
paginationContainer: {
flatListV: {
flex: 1,
},
paginationContainerH: {
position: 'absolute',
bottom: 16,
left: 16,
right: 16,
justifyContent: 'center',
},
paginationDots: {
paginationContainerV: {
position: 'absolute',
bottom: 16,
right: 8,
top: 16,
justifyContent: 'center',
},
poojan010 marked this conversation as resolved.
Show resolved Hide resolved
paginationSafeAreaH: {

},
paginationSafeAreaV: {
position: 'absolute',
right: 8,
bottom: 16,
top: 16,
justifyContent: "center"
},
poojan010 marked this conversation as resolved.
Show resolved Hide resolved
paginationDotsH: {
height: 16,
margin: 16,
flexDirection: isAndroidRTL ? 'row-reverse' : 'row',
justifyContent: 'center',
alignItems: 'center',
},
dot: {
paginationDotsV: {
width: 16,
margin: 10,
justifyContent: 'center',
alignItems: 'center',
},
dotH: {
width: 10,
height: 10,
borderRadius: 5,
marginHorizontal: 4,
},
dotV: {
poojan010 marked this conversation as resolved.
Show resolved Hide resolved
width: 10,
height: 10,
borderRadius: 5,
marginVertical: 4,
},
leftButtonContainer: {
position: 'absolute',
left: 0,
Expand All @@ -359,6 +455,14 @@ const styles = StyleSheet.create({
position: 'absolute',
right: 0,
},
topButtonContainer: {
position: 'absolute',
top: 0,
},
bottomButtonContainer: {
poojan010 marked this conversation as resolved.
Show resolved Hide resolved
position: 'absolute',
bottom: 0,
},
bottomButton: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, .3)',
Expand Down