diff --git a/README.md b/README.md index c72fbe0..94b2c5a 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ Fully controllable, high performance pager component w/ gesture support for React Native -

- +

+ +

# Installation @@ -26,14 +27,14 @@ There are additional steps to setting these up: _These examples were inspired by the docs of the awesome [react-native-snap-carousel library](https://github.com/archriss/react-native-snap-carousel)_ -

- - +

+ +

-

- - +

+ +

### Basic Pager @@ -162,7 +163,7 @@ function Buttons({ activeIndex, onChange }) { ## Pager ```typescript -import { Pager } from 'react-native-pager-component' +import { Pager } from '@crowdlinker/react-native-pager' Props -------- @@ -191,6 +192,43 @@ clampDrag: { } ``` +## Pagination + +```typescript +import { Pagination } from '@crowdlinker/react-native-pager' + +Props +-------- +children: React.ReactNode; +animatedIndex: Animated.Value; +pageInterpolation: iPageInterpolation; +style?: ViewStyle; +``` + +## Slider + +```typescript +import { Slider } from '@crowdlinker/react-native-pager' + +Props +-------- +numberOfScreens: number; +animatedIndex: Animated.Value; +style: ViewStyle; +``` + +## Progress + +```typescript +import { Progress } from '@crowdlinker/react-native-pager' + +Props +-------- +numberOfScreens: number; +animatedIndex: Animated.Value; +style: ViewStyle; +``` + ## Tabs and Stack Tab and Stack configurations are pretty straightforward to get setup: @@ -367,6 +405,7 @@ const swipeCards = { // you can pass a function as a transformer -- offset in this case will represent // the distance a screen is from the active screen // e.g -1 means 1 to the left, 4 means 4 to the right + // this will move cards downwards as they get further away from the activeIndex translateY: (offset: Animated.Value) => Animated.multiply(offset, 10), }, @@ -380,7 +419,7 @@ const swipeCards = { }, ], - // any Animated[fn] can be used in these functions + // any Animated function can be used in these zIndex: offset => floor(divide(offset, -1)), }; @@ -388,3 +427,75 @@ const swipeCards = { ... ; ``` + +## Pagination + +

+ +

+ +There's a few components to display the current active tab. These require an `animatedIndex` prop that you can pass into the active pager component and share with these components: + +```javascript + +const animatedIndex = new Value(0) + +function MyPager({ children }) { + + + {children} + + + // e.g: render this somewhere + + +} +``` + +There are three right now: ``, `` and ``. The API for these might change as it would be nice to have less opinionated and configurable components here. + +```javascript +// e.g emphasize active circle +const circleInterpolation = { + transform: [ + { + scale: { + inputRange: [-2, -1, 0, 1, 2], + outputRange: [0.5, 0.5, 0.8, 0.5, 0.5], + }, + }, + ], +}; + +// e.g - position circles vertically + + + + +; +``` + +```javascript + +``` + +```javascript + +``` diff --git a/docs/assets/paginated-tabs.gif b/docs/assets/paginated-tabs.gif new file mode 100644 index 0000000..e40e328 Binary files /dev/null and b/docs/assets/paginated-tabs.gif differ diff --git a/example/App.tsx b/example/App.tsx index 61ea086..66522de 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -70,10 +70,7 @@ const App = () => { return ( - - {children} - - + {children} ); diff --git a/example/tabs-stack.tsx b/example/tabs-stack.tsx index 31ddc59..db28106 100644 --- a/example/tabs-stack.tsx +++ b/example/tabs-stack.tsx @@ -1,6 +1,17 @@ import React, {useState} from 'react'; -import {StyleSheet, TouchableOpacity, View, Text} from 'react-native'; -import {Pager, Pagination, Slider} from '@crowdlinker/react-native-pager'; +import { + StyleSheet, + TouchableOpacity, + View, + Text, + ViewStyle, +} from 'react-native'; +import { + Pager, + Pagination, + Slider, + Progress, +} from '@crowdlinker/react-native-pager'; import Animated from 'react-native-reanimated'; const {Value, multiply} = Animated; @@ -63,7 +74,36 @@ function Stack({children}) { ); } -const animatedIndex = new Value(0); +const circleInterpolation = { + transform: [ + { + scale: { + inputRange: [-2, -1, 0, 1, 2], + outputRange: [0.5, 0.5, 0.8, 0.5, 0.5], + }, + }, + ], +}; + +function Circles({children, onChange}) { + return ( + + {React.Children.map(children, (_, i) => ( + + ))} + + ); +} + +const circlesContainer: ViewStyle = { + height: 20, + width: '70%', + alignSelf: 'center', + transform: [{translateY: -30}], +}; function Circle({i, onPress}) { return ( @@ -86,80 +126,67 @@ function Circle({i, onPress}) { ); } -const circleInterpolation = { - transform: [ - { - scale: { - inputRange: [-2, -1, 0, 1, 2], - outputRange: [0.5, 0.5, 0.8, 0.5, 0.5], - }, - }, - ], -}; +function Tabbar({children, onChange, activeIndex}) { + return ( + + {React.Children.map(children, (c, i) => ( + onChange(i)} + style={{ + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }}> + + {i} + + + ))} + + ); +} -function Tabs({ - children, - activeIndex: parentActiveIndex, - onChange: parentOnChange, - ...rest -}) { - const [_activeIndex, _onChange] = useState(0); +const animatedIndex = new Value(0); - const activeIndex = - parentActiveIndex !== undefined ? parentActiveIndex : _activeIndex; - const onChange = parentActiveIndex !== undefined ? parentOnChange : _onChange; +function Tabs({children}) { + const [activeIndex, onChange] = useState(0); + const activeColor = colors[activeIndex % colors.length]; return ( - + + onChange={onChange}> {children} - + + {children} + + + + - {React.Children.map(children, (c, i) => ( - - ))} - - - - - {React.Children.map(children, (c, i) => ( - onChange(i)} - style={{ - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }}> - - {i} - - - ))} - - - - + backgroundColor: activeColor, + }} + /> + + + {children} + + + ); } diff --git a/src/pager.tsx b/src/pager.tsx index 717f064..98c8b63 100644 --- a/src/pager.tsx +++ b/src/pager.tsx @@ -84,8 +84,6 @@ const { abs, lessThan, ceil, - interpolate, - concat, // @ts-ignore debug, } = Animated; diff --git a/src/pagination.tsx b/src/pagination.tsx index 80b8b1a..25e24c5 100644 --- a/src/pagination.tsx +++ b/src/pagination.tsx @@ -4,11 +4,11 @@ import { ViewStyle, LayoutChangeEvent } from 'react-native'; import { iPageInterpolation } from './pager'; import { memoize, mapConfigToStyle } from './util'; -const { sub, Value, divide, multiply } = Animated; +const { sub, Value, divide, multiply, add } = Animated; interface iPagination { children: React.ReactNode; - animatedIndex: Animated.Node; + animatedIndex: Animated.Value; pageInterpolation: iPageInterpolation; style?: ViewStyle; } @@ -48,7 +48,7 @@ function Pagination({ interface iPaginationItem { children: React.ReactNode; - animatedIndex: Animated.Node; + animatedIndex: Animated.Value; pageInterpolation: iPageInterpolation; index: number; style?: ViewStyle; @@ -73,10 +73,15 @@ function PaginationItem({ interface iSlider { numberOfScreens: number; - animatedIndex: Animated.Node; + animatedIndex: Animated.Value; style: ViewStyle; } +const DEFAULT_SLIDER_STYLE = { + height: 2, + backgroundColor: 'aquamarine', +}; + function Slider({ numberOfScreens, animatedIndex, style }: iSlider) { const width = memoize(new Value(0)); @@ -93,6 +98,35 @@ function Slider({ numberOfScreens, animatedIndex, style }: iSlider) { style={{ width: sliderWidth, transform: [{ translateX: translation }], + ...DEFAULT_SLIDER_STYLE, + ...style, + }} + /> + + ); +} + +function Progress({ numberOfScreens, animatedIndex, style }: iSlider) { + const width = memoize(new Value(0)); + + function handleLayout({ nativeEvent: { layout } }: LayoutChangeEvent) { + width.setValue(layout.width as any); + } + + const sliderWidth = divide( + width, + numberOfScreens, + divide(1, add(animatedIndex, 1)) + ); + + return ( + + @@ -100,4 +134,4 @@ function Slider({ numberOfScreens, animatedIndex, style }: iSlider) { ); } -export { Pagination, Slider }; +export { Pagination, Slider, Progress };