├── .gitignore
├── CHANGELOG.md
├── README.md
├── components
├── Buttons.js
├── PageData.js
├── PageDots.js
└── Paginator.js
├── images
├── 1.png
├── 2.png
└── 3.png
├── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.1.1 (October 11, 2016)
2 |
3 | * Detect light background and adapt the text and controls to it.
4 | * Allow to disable the bottom bar overlay via the `bottomOverlay` prop.
5 | * Allow to disable either of the skip, next, or done buttons.
6 |
7 | ## 0.1.0 (October 10, 2016)
8 |
9 | Initial release.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ``
2 |
3 | Onboarding experience made a breeze.
4 |
5 | Originally inspired by [AndroidOnboarder](https://github.com/chyrta/AndroidOnboarder).
6 |
7 | ## Quick demo
8 |
9 | |  |  |  |
10 | | --- | --- | --- |
11 | | Adapts to bright backgrounds | and dark, too | shows the Done button |
12 |
13 | ```javascript
14 | , title: 'Simple Messenger UI', subtitle: 'Implemented in React Native' },
17 | { backgroundColor: "#fe6e58", image: , title: 'Welcome', subtitle: 'To Earth' },
18 | { backgroundColor: "#999", image: , title: 'Also', subtitle: 'Mars is nice' },
19 | ]}
20 | onEnd={}
21 | />
22 | ```
23 |
24 | ## Install
25 |
26 | ```
27 | npm install --save react-native-simple-onboarding
28 | ```
29 |
30 | ```javascript
31 | import Onboarding from 'react-native-simple-onboarding';
32 | ```
33 |
34 | ## Usage
35 |
36 | ## `` component
37 |
38 | Props:
39 |
40 | * `pages` (required): an array of onboarding pages. A page is an object of shape:
41 | * `backgroundColor` (required): a background color for the page
42 | * `image` (required): a component instance displayed at the top of the page
43 | * `title` (required): a string title
44 | * `subtitle` (required): a string subtitle
45 | * `onEnd` (optional): a callback that is fired after the onboarding is complete
46 | * `bottomOverlay` (optional): a bool flag indicating whether the bottom bar overlay should be shown. Defaults to `true`.
47 | * `showSkip` (optional): a bool flag indicating whether the Skip button should be show. Defaults to `true`.
48 | * `showNext` (optional): a bool flag indicating whether the Next arrow button should be show. Defaults to `true`.
49 | * `showDone` (optional): a bool flag indicating whether the Done checkmark button should be show. Defaults to `true`.
50 |
51 | ## To Do
52 |
53 | * animations
54 | * accessibility
55 |
56 | ## License
57 |
58 | MIT.
59 |
--------------------------------------------------------------------------------
/components/Buttons.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, TouchableOpacity, Text } from 'react-native';
3 |
4 | const SymbolButton = ({ isLight, size, onPress, style, textStyle, children }) => (
5 |
6 |
7 | {children}
8 |
9 |
10 | );
11 |
12 | const TextButton = ({ isLight, size, onPress, textStyle, children }) => (
13 |
14 |
15 | {children}
16 |
17 |
18 | );
19 |
20 | export { SymbolButton, TextButton };
21 |
--------------------------------------------------------------------------------
/components/PageData.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text } from 'react-native';
3 |
4 | const Page = ({ width, height, children }) => (
5 |
6 | {children}
7 |
8 | );
9 |
10 | const PageContent = ({ children }) => (
11 |
12 |
13 | {children}
14 |
15 |
16 | );
17 |
18 | const PageData = ({ isLight, image, title, subtitle, titleStyles, subtitleStyles, ...rest }) => (
19 |
20 |
21 |
22 | {image}
23 |
24 |
25 | {title}
26 |
27 |
28 | {subtitle}
29 |
30 |
31 |
32 | );
33 |
34 | const styles = {
35 | content: {
36 | flex: 1,
37 | flexDirection: 'column',
38 | alignItems: 'center',
39 | justifyContent: 'center',
40 | },
41 | image: {
42 | flex: 0,
43 | paddingBottom: 60,
44 | alignItems: 'center',
45 | },
46 | title: {
47 | textAlign: 'center',
48 | fontSize: 26,
49 | color: '#fff',
50 | paddingBottom: 15,
51 | },
52 | titleLight: {
53 | color: '#000',
54 | },
55 | subtitle: {
56 | textAlign: 'center',
57 | fontSize: 16,
58 | color: 'rgba(255, 255, 255, 0.7)',
59 | },
60 | subtitleLight: {
61 | color: 'rgba(0, 0, 0, 0.7)',
62 | },
63 | };
64 |
65 | export default PageData;
66 |
--------------------------------------------------------------------------------
/components/PageDots.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text } from 'react-native';
3 |
4 | const PageCheckmark = ({ style }) => (
5 | ✓
6 | );
7 |
8 | const PageDot = ({ isLight, selected }) => (
9 |
16 | );
17 |
18 | const PageDots = ({ isLight, pages, currentPage }) => (
19 |
20 |
21 | {Array.from(new Array(pages), (x, i) => i).map(page => (
22 |
23 | ))}
24 |
25 |
26 | );
27 |
28 | const styles = {
29 | container: {
30 | flex: 0,
31 | flexDirection: 'row',
32 | alignItems: 'center',
33 | },
34 | element: {
35 | marginHorizontal: 3,
36 | },
37 | elementCheck: {
38 | textAlign: 'center',
39 | fontSize: 12,
40 | fontWeight: '900',
41 | },
42 | elementDot: {
43 | width: 6,
44 | height: 6,
45 | borderRadius: 3,
46 | },
47 | };
48 |
49 | export default PageDots;
50 |
--------------------------------------------------------------------------------
/components/Paginator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, TouchableOpacity, Text } from 'react-native';
3 |
4 | import PageDots from './PageDots';
5 | import { SymbolButton, TextButton } from './Buttons';
6 |
7 | const getDefaultStyle = (isLight) => ({
8 | color: isLight ? 'rgba(0, 0, 0, 0.8)' : '#fff',
9 | });
10 |
11 | const SkipButton = ({ isLight, ...props }) => (
12 |
13 | Skip
14 |
15 | );
16 |
17 | const NextButton = ({ isLight, ...props }) => (
18 |
19 | →
20 |
21 | );
22 | const DoneButton = ({ isLight, size, ...props }) => (
23 |
24 | ✓
25 |
26 | );
27 |
28 | const BUTTON_SIZE = 40;
29 | const Paginator = ({ isLight, overlay, showSkip, showNext, showDone, pages, currentPage, onEnd, onNext }) => (
30 |
31 |
32 | {showSkip && currentPage + 1 !== pages ?
33 | :
34 | null
35 | }
36 |
37 |
38 |
39 | {currentPage + 1 === pages ?
40 | (showDone ? : null) :
41 | (showNext ? : null)
42 | }
43 |
44 |
45 | );
46 |
47 | const styles = {
48 | container: {
49 | height: 60,
50 | paddingHorizontal: 0,
51 | flexDirection: 'row',
52 | justifyContent: 'space-between',
53 | alignItems: 'center',
54 | },
55 | containerOverlay: {
56 | backgroundColor: 'rgba(0, 0, 0, 0.1)',
57 | },
58 | buttonLeft: {
59 | width: 70,
60 | justifyContent: 'flex-start',
61 | alignItems: 'center',
62 | },
63 | buttonRight: {
64 | width: 70,
65 | justifyContent: 'flex-end',
66 | alignItems: 'center',
67 | }
68 | };
69 |
70 | export default Paginator;
71 |
--------------------------------------------------------------------------------
/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goshacmd/react-native-simple-onboarding/8b7c1f981eeb4b7bd499f8fab0ad636c6686deec/images/1.png
--------------------------------------------------------------------------------
/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goshacmd/react-native-simple-onboarding/8b7c1f981eeb4b7bd499f8fab0ad636c6686deec/images/2.png
--------------------------------------------------------------------------------
/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goshacmd/react-native-simple-onboarding/8b7c1f981eeb4b7bd499f8fab0ad636c6686deec/images/3.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { View, ScrollView, Dimensions, Text, TouchableOpacity, StyleSheet } from 'react-native';
4 | import tinycolor from 'tinycolor2';
5 |
6 | import PageData from './components/PageData';
7 | import Paginator from './components/Paginator';
8 |
9 | export default class Onboarding extends Component {
10 | constructor() {
11 | super();
12 |
13 | this.state = {
14 | currentPage: 0,
15 | };
16 | }
17 |
18 | updatePosition = (event) => {
19 | const { contentOffset, layoutMeasurement } = event.nativeEvent;
20 | const pageFraction = contentOffset.x / layoutMeasurement.width;
21 | const page = Math.round(pageFraction);
22 | const isLastPage = this.props.pages.length === page + 1;
23 | if (isLastPage && pageFraction - page > 0.3) {
24 | this.props.onEnd();
25 | } else {
26 | this.setState({ currentPage: page });
27 | }
28 | };
29 |
30 | goNext = () => {
31 | const { width } = Dimensions.get('window');
32 | const { currentPage } = this.state;
33 | const nextPage = currentPage + 1;
34 | const offsetX = nextPage * width;
35 | this.refs.scroll.scrollTo({
36 | x: offsetX,
37 | animated: true
38 | }, () => {
39 | this.setState({ currentPage: nextPage });
40 | });
41 | };
42 |
43 | render() {
44 | const { width, height } = Dimensions.get('window');
45 | const { pages, bottomOverlay, showSkip, showNext, showDone } = this.props;
46 | const currentPage = pages[this.state.currentPage] || pages[0];
47 | const { backgroundColor } = currentPage;
48 | const isLight = tinycolor(backgroundColor).getBrightness() > 180;
49 |
50 | return (
51 |
52 |
60 | {pages.map(({ image, title, subtitle, titleStyles, subtitleStyles }, idx) => (
61 |
72 | ))}
73 |
74 |
85 |
86 | );
87 | }
88 | }
89 |
90 | Onboarding.propTypes = {
91 | pages: PropTypes.arrayOf(PropTypes.shape({
92 | backgroundColor: PropTypes.string.isRequired,
93 | image: PropTypes.element.isRequired,
94 | title: PropTypes.string.isRequired,
95 | subtitle: PropTypes.string.isRequired,
96 | })).isRequired,
97 | bottomOverlay: PropTypes.bool,
98 | showSkip: PropTypes.bool,
99 | showNext: PropTypes.bool,
100 | showDone: PropTypes.bool,
101 | };
102 |
103 | Onboarding.defaultProps = {
104 | bottomOverlay: true,
105 | showSkip: true,
106 | showNext: true,
107 | showDone: true,
108 | };
109 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-simple-onboarding",
3 | "version": "0.1.1",
4 | "description": "A simple onboarding component for React Native",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 0"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/goshakkk/react-native-simple-onboarding.git"
12 | },
13 | "keywords": [
14 | "React",
15 | "React",
16 | "Native",
17 | "React-Native",
18 | "Component",
19 | "Onboarding",
20 | "iOS",
21 | "Android"
22 | ],
23 | "author": "Gosha Arinich",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/goshakkk/react-native-simple-onboarding/issues"
27 | },
28 | "homepage": "https://github.com/goshakkk/react-native-simple-onboarding#readme",
29 | "dependencies": {
30 | "tinycolor2": "^1.4.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------