Collapsible tab view component for React Native
npm install react-native-collapsible-tab-view-scroll-fix
|
|
|
|
sh
yarn add react-native-collapsible-tab-view-scroll-fix react-native-pager-view
`
Then, add Reanimated, follow the official installation guide.
Quick Start
`tsx
import React from 'react'
import { View, StyleSheet, ListRenderItem } from 'react-native'
import { Tabs } from 'react-native-collapsible-tab-view-scroll-fix'
const HEADER_HEIGHT = 250
const DATA = [0, 1, 2, 3, 4]
const identity = (v: unknown): string => v + ''
const Header = () => {
return
}
const Example: React.FC = () => {
const renderItem: ListRenderItem = React.useCallback(({ index }) => {
return (
)
}, [])
return (
renderHeader={Header}
headerHeight={HEADER_HEIGHT} // optional
>
data={DATA}
renderItem={renderItem}
keyExtractor={identity}
/>
)
}
const styles = StyleSheet.create({
box: {
height: 250,
width: '100%',
},
boxA: {
backgroundColor: 'white',
},
boxB: {
backgroundColor: '#D8D8D8',
},
header: {
height: HEADER_HEIGHT,
width: '100%',
backgroundColor: '#2196f3',
},
})
export default Example
`
Guides
Scrolling on the Header
To enable scrolling from the header, follow these steps:
- If the HeaderComponent does not contain touchable components, set the pointerEvents prop to 'none'.
- If the HeaderComponent does contain touchable components, set the pointerEvents prop to 'box-none' to ensure they function properly.
Note: If any child component within the HeaderComponent should not respond to touches, such as an element, set its pointerEvents prop to 'none'. Otherwise, it may unintentionally become the target of a touch gesture on iOS devices and prevent scrolling.
API Reference
Core
$3
Basic usage looks like this:
`tsx
import { Tabs } from 'react-native-collapsible-tab-view-scroll-fix'
const Example = () => {
return (
)
}
`
#### Props
|name|type|default|description|
|:----:|:----:|:----:|:----:|
|allowHeaderOverscroll|boolean \| undefined|false|Whether the header moves down during overscrolling (for example on pull-to-refresh on iOS) or sticks to the top|
|cancelLazyFadeIn|boolean \| undefined|||
|cancelTranslation|boolean \| undefined|||
|containerStyle|StyleProp|||
|headerContainerStyle|StyleProp|||
|headerHeight|number \| undefined||Is optional, but will optimize the first render.|
|initialTabName|string \| undefined|||
|lazy|boolean \| undefined||If lazy, will mount the screens only when the tab is visited. There is a default fade in transition.|
|minHeaderHeight|number \| undefined||Header minimum height when collapsed|
|onIndexChange|((index: number) => void) \| undefined||Callback fired when the index changes. It receives the current index.|
|onTabChange|(data: { prevIndex: number index: number prevTabName: T tabName: T }) => void||Callback fired when the tab changes. It receives the previous and current index and tabnames.|
|pagerProps|Omit||Props passed to the pager. If you want for example to disable swiping, you can pass { scrollEnabled: false }|
|renderHeader|(props: TabBarProps|||
|renderTabBar|(props: TabBarProps|(props: TabBarProps||
|revealHeaderOnScroll|boolean \| undefined||Reveal header when scrolling down. Implements diffClamp.|
|snapThreshold|number \| null \| undefined|null|Percentage of header height to define as the snap point. A number between 0 and 1, or null to disable snapping.|
|tabBarHeight|number \| undefined||Is optional, but will optimize the first render.|
|width|number \| undefined||Custom width of the container. Defaults to the window width.|
$3
Wrap your screens with Tabs.Tab. Basic usage looks like this:
`tsx
`
#### Props
|name|type|
|:----:|:----:|
|label|string \| ((props: TabItemProps|
|name|T|
$3
Typically used internally, but if you want to mix lazy and regular screens you can wrap the lazy ones with this component.
#### Props
|name|type|
|:----:|:----:|
|cancelLazyFadeIn|boolean \| undefined|
|startMounted|boolean \| undefined|
$3
Use like a regular FlatList.
$3
Use like a regular FlashList.
$3
Use like a regular MasonryFlashList.
$3
Use like a regular ScrollView.
$3
Use like a regular SectionList.
$3
You can pass a ref to Tabs.Container.
`tsx
const ref = React.useRef()
`
| method | type |
| :-------------: | :--------------------------: |
| jumpToTab | (name: T) => boolean |
| setIndex | (index: number) => boolean |
| getFocusedTab | () => T |
| getCurrentIndex | () => number |
Hooks
$3
This hook provides access to key styles for the collapsible tab view. It can be used to obtain the progressViewOffset and pass it to the RefreshControl of the scroll view.
`tsx
const {
contentContainerStyle,
progressViewOffset,
style,
} = useCollapsibleStyle()
`
#### Values
| name | type |
| :-------------------: | :------------------------------------------: |
| contentContainerStyle | { minHeight: number; paddingTop: number; } |
| progressViewOffset | number |
| style | { width: number; } |
$3
This hook returns an animated value representing the current tab index. As the tab view can be in between panes while swiping, this value is a floating-point number.
`tsx
const tabIndex = useAnimatedTabIndex()
`
$3
This hook returns the name of the currently focused tab.
`tsx
const focusedTab = useFocusedTab()
`
$3
This hook returns the top distance and the header height. For an example of how to use this, check out the animated header example in the example folder.
`tsx
const { top, height } = useHeaderMeasurements()
`
$3
This hook returns the vertical scroll position of the current tab as an Animated SharedValue.
Since this library requires handling the onScroll event for its functionality, this is the only way to react to changes in the scroll position of the underlying scrollable component.
`tsx
const scrollY = useCurrentTabScrollY()
`
Default Tab Bar
$3
Any additional props are passed to the pressable component.
#### Props
|name|type|description|
|:----:|:----:|:----:|
|activeColor|string \| undefined|Color applied to the label when active|
|inactiveColor|string \| undefined|Color applied to the label when inactive|
|inactiveOpacity|number \| undefined||
|index|number||
|indexDecimal|SharedValue||
|label|string \| ((props: TabItemProps||
|labelStyle|StyleProp|Style to apply to the tab item label|
|name|T||
|onLayout|(((event: LayoutChangeEvent) => void) & ((event: LayoutChangeEvent) => void)) \| undefined|Invoked on mount and layout changes with {nativeEvent: { layout: {x, y, width, height}}}.|
|onPress|(name: T) => void||
|pressColor|string \| undefined||
|pressOpacity|number \| undefined||
|scrollEnabled|boolean \| undefined||
|style|StyleProp|Either view styles or a function that receives a boolean reflecting whether the component is currently pressed and returns view styles.|
Known Issues
Android FlatList Pull to Refresh
Refer to this open issue. We utilize scrollTo to synchronize the unfocused tabs. While it is intended for use with ScrollView, it works well with FlatList, until the RefreshControl is added. Note that this issue occurs only on Android.
Workaround: Check out the Android Shared Pull To Refresh example in the expo app. You can implement a single pull-to-refresh for the Tabs.Container.
iOS FlatList StickyHeaderIndices and iOS SectionList StickySectionHeadersEnabled
When using the stickyHeaderIndices prop on a FlatList or stickySectionHeadersEnabled on a SectionList, the sticky elements do not scroll up as the header collapses. This issue is specific to iOS.
See #136.
ref.setIndex
This is not an issue per se, but it's essential to be aware of it. When using containerRef.current.setIndex(i), if you set it to the current index, the screen will scroll to the top. You can prevent this behavior as follows:
`ts
const index = pageRef.current?.getCurrentIndex()
if (index !== nextIndex) {
pageRef.current?.setIndex(nextIndex)
}
`
Alternative Libraries
If you do not require a full-featured tab view, consider another option: a simple segmented control / material tab bar without swiping or snapping, using only the React Native Animated API.
- react-native-collapsible-segmented-view
Contributing and running the Example
While developing, you can run the example app to test your changes.
First run yarn in root:
`sh
yarn
`
Then prepare the example:
`sh
cd example
yarn
`
Then run the example:
`
yarn ios
`
Please follow the angular commit message format.
Make sure your code passes TypeScript and ESLint. Run the following to verify:
`sh
yarn typescript
yarn lint
`
To fix formatting errors, run the following:
`sh
yarn lint -- --fix
`
Documentation changes
Edit the README_TEMPLATE, or update the docstrings inside the src folder, and run:
`sh
yarn docs
``