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
Show file tree
Hide file tree
Changes from all 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
Binary file added Images/autoPlay.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/vertical_render.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ yarn add react-native-app-intro-slider
| ---------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------ |
| ![Basic example gif](Images/basic-example.gif) | ![showSkipButton example image](Images/skipbutton-example.jpg) | ![bottomButton example image](Images/bottomskipbutton-example.jpg) |


`Vertical Render` | `Auto Play`
| ---------------------------------------------- | --------------------------------------------------------------
| ![Basic example gif](Images/vertical_render.gif) | ![showSkipButton example image](Images/autoPlay.gif) |

The component is based on FlatList so usage is very similar. Pass a data-array to AppIntroSlider along with a `renderItem`-function:

```javascript
import React from 'react';
import { StyleSheet } from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
import AppIntroSlider, { Orientation } from 'react-native-app-intro-slider';

const slides = [
{
Expand Down Expand Up @@ -133,6 +138,7 @@ export default class App extends React.Component {
return (
<AppIntroSlider
data={slides}
orientation={Orientation.HORIZONTAL}
renderDoneButton={this._renderDoneButton}
renderNextButton={this._renderNextButton}
/>
Expand Down Expand Up @@ -162,10 +168,12 @@ The component extends `FlatList` so all FlatList-props are valid.
| dotStyle | `style` | {backgroundColor: 'rgba(0, 0, 0, .2)'} | Style of inactive pagination dots |
| dotClickEnabled | `boolean` | `true` | Whether users can navigate using the pagination dots |
| activeDotStyle | `style` | {backgroundColor: 'rgba(255, 255, 255, .9)'} | Style of active pagination dot |
| orientation | enum | HORIZONTAL | defines the orientation of the intro slides |
| skipLabel | `string` | `Skip` | Custom label for Skip button |
| doneLabel | `string` | `Done` | Custom label for Done button |
| nextLabel | `string` | `Next` | Custom label for Next button |
| prevLabel | `string` | `Back` | Custom label for Prev button |
| autoPlay | `boolean` | `false` | When true slides starts playing automatically |
| showSkipButton | `boolean` | `false` | Enable to show a skip button to the left of pagination dots. When `bottomButton == true` the skip button is a small text under the full-width next button |
| showPrevButton | `boolean` | `false` | Enable to show a previous button. If `showSkipButton` is true, the skip button will be displayed on the first page and prev button on subsequent one |
| showNextButton | `boolean` | `true` | Disable to hide the next button |
Expand Down
138 changes: 111 additions & 27 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import mergeExtraData from './merge-extradata';

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


export enum Orientation {
HORIZONTAL,
VERTICAL
}

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

type State = {
Expand Down Expand Up @@ -75,24 +83,51 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
showPrevButton: false,
showSkipButton: false,
bottomButton: false,
orientation: Orientation.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 === Orientation.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 +176,22 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
name: string,
onPress?: (e: GestureResponderEvent) => void,
) => {
const leftButtonStyle = this.props.orientation === Orientation.VERTICAL
? styles.topButtonContainer
: styles.leftButtonContainer
const rightButtonStyle = this.props.orientation === Orientation.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 +219,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,11 +248,16 @@ export default class AppIntroSlider<ItemT = any> extends React.Component<
const primaryButton = isLastSlide
? this._renderDoneButton()
: this._renderNextButton();

const containerStyle = this.props.orientation === Orientation.VERTICAL
? styles.paginationContainerV
: styles.paginationContainerH
const dotsContainerStyle = this.props.orientation === Orientation.VERTICAL
? styles.paginationDotsV
: styles.paginationDotsH
return (
<View style={styles.paginationContainer}>
<SafeAreaView>
<View style={styles.paginationDots}>
<View style={containerStyle}>
<SafeAreaView style={styles.paginationSafeArea}>
<View style={dotsContainerStyle}>
{this.props.data.length > 1 &&
this.props.data.map((_, i) =>
this.props.dotClickEnabled ? (
Expand Down Expand Up @@ -244,21 +291,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 === Orientation.HORIZONTAL
? e.nativeEvent.contentOffset.x
: e.nativeEvent.contentOffset.y;
const total = this.props.orientation === 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 +321,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 === Orientation.VERTICAL ? height : width),
animated: false,
});
};
Expand All @@ -290,23 +343,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 === Orientation.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 === Orientation.HORIZONTAL}
pagingEnabled
showsHorizontalScrollIndicator={false}
bounces={false}
style={styles.flatList}
style={flatListStyle}
renderItem={this._renderItem}
onMomentumScrollEnd={this._onMomentumScrollEnd}
extraData={extra}
Expand All @@ -327,29 +383,49 @@ 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
paginationSafeArea: {
flex : 1,
justifyContent: "center"
},
paginationDotsH: {
height: 16,
margin: 16,
flexDirection: isAndroidRTL ? 'row-reverse' : 'row',
justifyContent: 'center',
alignItems: 'center',
},
dot: {
paginationDotsV: {
width: 16,
margin: 10,
justifyContent: 'center',
alignItems: 'center',
},
dot :{
width: 10,
height: 10,
borderRadius: 5,
marginHorizontal: 4,
margin : 5
},
leftButtonContainer: {
position: 'absolute',
Expand All @@ -359,6 +435,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