Skip to content

Tutorial inside scrollview. #111

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

Open
wants to merge 12 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
84 changes: 68 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,11 @@
<h1 align="center">React Native Copilot</h1>

<div align="center">
<p align="center">
<a href="https://semaphoreci.com/okgrow/react-native-co-pilot">
<img src="https://semaphoreci.com/api/v1/okgrow/react-native-co-pilot/branches/master/shields_badge.svg" alt="Build Status" />
</a>
<a href="https://www.npmjs.com/package/@okgrow/react-native-copilot">
<img src="https://img.shields.io/npm/v/@okgrow/react-native-copilot.svg" alt="NPM Version" />
</a>
<a href="https://www.npmjs.com/package/@okgrow/react-native-copilot">
<img src="https://img.shields.io/npm/dm/@okgrow/react-native-copilot.svg" alt="NPM Downloads" />
</a>
</p>
</div>

<p align="center">
Step-by-step walkthrough for your react native app!
</p>

<p align="center">
<img src="https://media.giphy.com/media/65VKIzGWZmHiEgEBi7/giphy.gif" alt="React Native Copilot" />
<img src="https://media.giphy.com/media/4ZvoxeFdW7jeSETYsX/giphy.gif" alt="React Native Copilot" />
</p>

<p align="center">
Expand Down Expand Up @@ -115,7 +101,40 @@ copilot({
animated: true, // or false
})(RootComponent);
```
### Custom component over the overlay
This comes in handy when you want to show some kind of icon (like swipe gesture) on top of the overlay. The CopilotStep component accepts a prop called "overlayElement" that must return a react component wrapped inside a function. The position, size, nextHandle and previousHandle are automatically injected into this function.

```js
import { React, { Component } } from "react";
import { View } from "react-native";

class Example extends Component {
renderOverlayElement = (position, size, handleNext, handlePrevious) => {
//Displays this in the center of the overlay
return (
<View style={{
position: "absolute",
top: position.y + size.y/3
left: position.x + size.x/3
}}>
//...
</View>
);
}
render(){
<CopilotStep
name={}
text={}
order={}
{...overlayElement && {
overlayElement: this.renderOverlayElement
}}
>
//...
</CopilotStep>
}
}
```
### Custom tooltip component
You can customize the tooltip by passing a component to the `copilot` HOC maker. If you are looking for an example tooltip component, take a look at [the default tooltip implementation](https://github.com/okgrow/react-native-copilot/blob/master/src/components/Tooltip.js).

Expand Down Expand Up @@ -188,6 +207,34 @@ class HomeScreen {
### Triggering the tutorial
Use `this.props.start()` in the root component in order to trigger the tutorial. You can either invoke it with a touch event or in `componentDidMount`. Note that the component and all its descendants must be mounted before starting the tutorial since the `CopilotStep`s need to be registered first.

### Usage inside a ScrollView
Pass the ScrollView reference as the second argument to the `this.props.start()` function.
eg `this.props.start(false, ScrollViewRef)`

```js
import { ScrollView } from 'react-native';
import { copilot } from '@okgrow/react-native-copilot';

class HomeScreen {
componentDidMount() {
// Starting the tutorial and passing the scrollview reference.
this.props.start(false, this.scrollView);
}

componentWillUnmount() {
// Don't forget to disable event handlers to prevent errors
this.props.copilotEvents.off('stop');
}

render() {
<ScrollView ref={ref => this.scrollView = ref}>
// ...
</ScrollView>
}
}
export default copilot()(HomeScreen);
```

### Listening to the events
Along with `this.props.start()`, `copilot` HOC passes `copilotEvents` function to the component to help you with tracking of tutorial progress. It utilizes [mitt](https://github.com/developit/mitt) under the hood, you can see how full API there.

Expand All @@ -200,12 +247,15 @@ List of available events is:

**Example:**
```js
import { ScrollView } from 'react-native';
import { copilot, CopilotStep } from '@okgrow/react-native-copilot';

const CustomComponent = ({ copilot }) => <View {...copilot}><Text>Hello world!</Text></View>;

class HomeScreen {
componentDidMount() {
// Starting the tutorial.
this.props.start(false, this.scrollView);
this.props.copilotEvents.on('stop', () => {
// Copilot tutorial finished!
});
Expand All @@ -217,7 +267,9 @@ class HomeScreen {
}

render() {
// ...
<ScrollView ref={ref => this.scrollView = ref}>
// ...
</ScrollView>
}
}
```
Expand Down
42 changes: 29 additions & 13 deletions src/components/ConnectedCopilotStep.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// @flow
import React, { Component } from 'react';
import React, { Component } from "react";

import type { CopilotContext } from '../types';
import type { CopilotContext, OverLayElement } from "../types";

type Props = {
name: string,
text: string,
order: number,
overlayElement?: OverLayElement,
active?: boolean,
_copilot: CopilotContext,
children: React$Element
};

class ConnectedCopilotStep extends Component<Props> {
static defaultProps = {
active: true,
active: true
};

componentDidMount() {
Expand Down Expand Up @@ -48,6 +49,9 @@ class ConnectedCopilotStep extends Component<Props> {
order: this.props.order,
target: this,
wrapper: this.wrapper,
...(this.props.overlayElement && {
overlayElement: this.props.overlayElement
})
});
}

Expand All @@ -56,21 +60,31 @@ class ConnectedCopilotStep extends Component<Props> {
}

measure() {
if (typeof __TEST__ !== 'undefined' && __TEST__) { // eslint-disable-line no-undef
return new Promise(resolve => resolve({
x: 0, y: 0, width: 0, height: 0,
}));
if (typeof __TEST__ !== "undefined" && __TEST__) {
// eslint-disable-line no-undef
return new Promise(resolve =>
resolve({
x: 0,
y: 0,
width: 0,
height: 0
})
);
}

return new Promise((resolve, reject) => {
const measure = () => {
// Wait until the wrapper element appears
if (this.wrapper.measure) {
this.wrapper.measure(
(ox, oy, width, height, x, y) => resolve({
x, y, width, height,
}),
reject,
(ox, oy, width, height, x, y) =>
resolve({
x,
y,
width,
height
}),
reject
);
} else {
requestAnimationFrame(measure);
Expand All @@ -83,8 +97,10 @@ class ConnectedCopilotStep extends Component<Props> {

render() {
const copilot = {
ref: (wrapper) => { this.wrapper = wrapper; },
onLayout: () => { }, // Android hack
ref: wrapper => {
this.wrapper = wrapper;
},
onLayout: () => {} // Android hack
};

return React.cloneElement(this.props.children, { copilot });
Expand Down
Loading