Skip to content

Commit

Permalink
feat: export page content props (#21)
Browse files Browse the repository at this point in the history
* chore: extract page content props to allow more customisations

* chore: updated custom view example

* docs: updated readme file
  • Loading branch information
gorhom authored Jun 6, 2020
1 parent c1da684 commit 222f1c4
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 91 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,17 @@ const Screen = () => {

#### PaperOnboardingItemType

| name | description | required | type |
| ---------------- | --------------------------------------------------------------------------------------------- | -------- | ------------------------------------------ |
| content | Slide/page content, this will replace default content. | NO | (() => React.ReactNode) \| React.ReactNode |
| image | Image cover. | NO | (() => React.ReactNode) \| React.ReactNode |
| icon | Indicator icon. | NO | (() => React.ReactNode) \| React.ReactNode |
| backgroundColor | Background color. | YES | string |
| title | Title text. | NO | string |
| description | Description text. | NO | string |
| titleStyle | Text style to override page/slide title default style. | NO | StyleProp<TextStyle> |
| descriptionStyle | Text style to override page/slide description default style. | NO | StyleProp<TextStyle> |
| showCloseButton | Show close button when page/slide is active, _note: last page will always show close button._ | NO | boolean |
| name | description | required | type |
| ---------------- | --------------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------- |
| content | Slide/page content, this will replace default content. | NO | ((props: [PageContentProps](./src/types.ts#L87)) => React.ReactNode) \| React.ReactNode |
| image | Image cover. | NO | (() => React.ReactNode) \| React.ReactNode |
| icon | Indicator icon. | NO | (() => React.ReactNode) \| React.ReactNode |
| backgroundColor | Background color. | YES | string |
| title | Title text. | NO | string |
| description | Description text. | NO | string |
| titleStyle | Text style to override page/slide title default style. | NO | StyleProp<TextStyle> |
| descriptionStyle | Text style to override page/slide description default style. | NO | StyleProp<TextStyle> |
| showCloseButton | Show close button when page/slide is active, _note: last page will always show close button._ | NO | boolean |

## Built With ❤️

Expand Down
86 changes: 77 additions & 9 deletions example/src/components/CustomView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import React from 'react';
import { View, Text, StyleSheet, Alert } from 'react-native';
import { View, Text, StyleSheet, Alert, ViewStyle } from 'react-native';
import Animated, { interpolate, Extrapolate } from 'react-native-reanimated';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { PageContentProps } from '@gorhom/paper-onboarding';
import { TouchableOpacityProps } from 'react-native';

const AnimatedTouchableOpacity: React.FC<Animated.AnimateProps<
ViewStyle,
TouchableOpacityProps
>> = Animated.createAnimatedComponent(TouchableOpacity) as any;

const styles = StyleSheet.create({
container: {
Expand Down Expand Up @@ -34,19 +42,79 @@ const styles = StyleSheet.create({
},
});

const CustomView = () => {
const CustomView = ({ animatedFocus }: PageContentProps) => {
//#region animations
const animatedBoxTranslateY = interpolate(animatedFocus, {
inputRange: [0, 1],
outputRange: [100, 0],
extrapolate: Extrapolate.CLAMP,
});
//#endregion

//#region styles
const boxStyle = [
styles.box,
{
transform: [
{ translateY: animatedBoxTranslateY },
] as Animated.AnimatedTransform,
},
];
const item1Style = [
styles.item,
{
opacity: interpolate(animatedFocus, {
inputRange: [0.5, 1],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP,
}),
},
];
const item2Style = [
styles.item,
{
opacity: interpolate(animatedFocus, {
inputRange: [0.625, 1],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP,
}),
},
];
const item3Style = [
styles.item2,
{
opacity: interpolate(animatedFocus, {
inputRange: [0.75, 1],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP,
}),
},
];
const buttonStyle = {
opacity: interpolate(animatedFocus, {
inputRange: [0.875, 1],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP,
}),
};
//#endregion

const handleButtonPress = () => {
Alert.alert('Button Clicked !');
};
return (
<View style={styles.container}>
<View style={styles.box} />
<View style={styles.item} />
<View style={styles.item} />
<View style={styles.item2} />
<TouchableOpacity onPress={handleButtonPress}>
<View
style={styles.container}
shouldRasterizeIOS={true}
needsOffscreenAlphaCompositing={true}
>
<Animated.View style={boxStyle} />
<Animated.View style={item1Style} />
<Animated.View style={item2Style} />
<Animated.View style={item3Style} />
<AnimatedTouchableOpacity style={buttonStyle} onPress={handleButtonPress}>
<Text style={styles.buttonText}>Button</Text>
</TouchableOpacity>
</AnimatedTouchableOpacity>
</View>
);
};
Expand Down
98 changes: 48 additions & 50 deletions src/components/page/Page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useMemo, useCallback, memo } from 'react';
import { Text } from 'react-native';
import { Svg, Circle } from 'react-native-svg';
import Animated from 'react-native-reanimated';
import { calculateRectangleCircleRadius } from '../../utils/math';
import PageContent from '../pageContent/PageContent';
import { PageProps } from '../../types';
import { styles } from './styles';

Expand Down Expand Up @@ -35,15 +35,20 @@ const PageComponent = ({
[screenDimensions, indicatorSize, safeInsets]
);

// animations
const animatedContentOpacity = interpolate(currentIndex, {
inputRange: [index - 0.5, index, index + 0.5],
//#region animation
const animatedFocus = interpolate(currentIndex, {
inputRange: [index - 1, index, index + 1],
outputRange: [0, 1, 2],
extrapolate: Extrapolate.CLAMP,
});
const animatedContentOpacity = interpolate(animatedFocus, {
inputRange: [0.5, 1, 1.5],
outputRange: [0, 1, 0],
extrapolate: Extrapolate.CLAMP,
});

const animatedContentTopPosition = interpolate(currentIndex, {
inputRange: [index - 1, index, index + 1],
const animatedContentTopPosition = interpolate(animatedFocus, {
inputRange: [0, 1, 2],
outputRange: [
screenDimensions.height / 8,
0,
Expand All @@ -52,14 +57,8 @@ const PageComponent = ({
extrapolate: Extrapolate.CLAMP,
});

const animatedImageTopPosition = interpolate(currentIndex, {
inputRange: [index - 1, index],
outputRange: [screenDimensions.height / 8, 0],
extrapolate: Extrapolate.CLAMP,
});

const animatedBackgroundSize = interpolate(currentIndex, {
inputRange: [index - 1, index],
const animatedBackgroundSize = interpolate(animatedFocus, {
inputRange: [0, 1],
outputRange: [0, backgroundExtendedSize],
extrapolate: Extrapolate.CLAMP,
});
Expand All @@ -69,8 +68,9 @@ const PageComponent = ({
indicatorSize / 2,
index * indicatorSize
);
//#endregion

// styles
//#region styles
const contentContainerStyle: any = useMemo(
() => [
styles.contentContainer,
Expand All @@ -90,40 +90,54 @@ const PageComponent = ({
indicatorSize,
]
);

const titleStyle = useMemo(
() => [
styles.title,
titleStyleOverride,
item.titleStyle ? item.titleStyle : null,
],
() => [titleStyleOverride, item.titleStyle ? item.titleStyle : null],
[item, titleStyleOverride]
);

const descriptionStyle = useMemo(
() => [
styles.description,
descriptionStyleOverride,
item.descriptionStyle ? item.descriptionStyle : null,
],
[item, descriptionStyleOverride]
);
//#endregion

const imageContainerStyle: any = useMemo(
() => [
styles.imageContainer,
{
transform: [{ translateY: animatedImageTopPosition }],
},
],
[animatedImageTopPosition]
);

//#region callbacks
const handleContainerRef = useCallback(ref => handleRef(ref, index), [
index,
handleRef,
]);
//#endregion

//#region memo
const pageContentProps = useMemo(
() => ({
index,
animatedFocus,
image: item.image,
title: item.title,
description: item.description,
titleStyle,
descriptionStyle,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[index, item, titleStyle, descriptionStyle]
);
//#endregion

const renderContent = useCallback(() => {
const ContentComponent: any = item.content;
return ContentComponent ? (
typeof ContentComponent === 'function' ? (
ContentComponent(pageContentProps)
) : (
<ContentComponent {...pageContentProps} />
)
) : (
<PageContent {...pageContentProps} />
);
}, [item, pageContentProps]);
return (
<Animated.View
pointerEvents={index === 0 ? 'auto' : 'none'}
Expand All @@ -141,23 +155,7 @@ const PageComponent = ({
/>
</Svg>
<Animated.View style={contentContainerStyle}>
{item.content ? (
typeof item.content === 'function' ? (
item.content()
) : (
item.content
)
) : (
<>
{item.image && (
<Animated.View style={imageContainerStyle}>
{typeof item.image === 'function' ? item.image() : item.image}
</Animated.View>
)}
<Text style={titleStyle}>{item.title}</Text>
<Text style={descriptionStyle}>{item.description}</Text>
</>
)}
{renderContent()}
</Animated.View>
</Animated.View>
);
Expand Down
17 changes: 0 additions & 17 deletions src/components/page/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,6 @@ export const styles = StyleSheet.create({
justifyContent: 'center',
paddingHorizontal: 34,
},
imageContainer: {
overflow: 'hidden',
marginBottom: 64,
},
title: {
textAlign: 'center',
color: 'white',
fontWeight: '600',
fontSize: 36,
marginBottom: 16,
},
description: {
color: 'white',
textAlign: 'center',
fontWeight: '400',
fontSize: 18,
},
background: {
...StyleSheet.absoluteFillObject,
},
Expand Down
62 changes: 62 additions & 0 deletions src/components/pageContent/PageContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useMemo, memo } from 'react';
import { Text, Dimensions } from 'react-native';
import Animated from 'react-native-reanimated';
import { PageContentProps } from '../../types';
import { styles } from './styles';

const { interpolate, Extrapolate } = Animated;

const SCREEN_HEIGHT = Dimensions.get('window').height;

const PageContentComponent = ({
animatedFocus,
image,
title,
description,
titleStyle: titleStyleOverride,
descriptionStyle: descriptionStyleOverride,
}: PageContentProps) => {
//#region
const animatedImageTopPosition = interpolate(animatedFocus, {
inputRange: [0, 1],
outputRange: [SCREEN_HEIGHT / 8, 0],
extrapolate: Extrapolate.CLAMP,
});
//#endregion

//#region styles
const titleStyle = useMemo(() => [styles.title, titleStyleOverride], [
titleStyleOverride,
]);

const descriptionStyle = useMemo(
() => [styles.description, descriptionStyleOverride],
[descriptionStyleOverride]
);

const imageContainerStyle: any = useMemo(
() => [
styles.imageContainer,
{
transform: [{ translateY: animatedImageTopPosition }],
},
],
[animatedImageTopPosition]
);
//#endregion
return (
<>
{image && (
<Animated.View style={imageContainerStyle}>
{typeof image === 'function' ? image() : image}
</Animated.View>
)}
<Text style={titleStyle}>{title}</Text>
<Text style={descriptionStyle}>{description}</Text>
</>
);
};

const PageContent = memo(PageContentComponent);

export default PageContent;
Empty file.
Loading

0 comments on commit 222f1c4

Please sign in to comment.