Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/pedrobern/react-native-collapsible-tab-view
A cross-platform Collapsible Tab View component for React Native
https://github.com/pedrobern/react-native-collapsible-tab-view
expo react react-native tabs tabview
Last synced: 5 days ago
JSON representation
A cross-platform Collapsible Tab View component for React Native
- Host: GitHub
- URL: https://github.com/pedrobern/react-native-collapsible-tab-view
- Owner: PedroBern
- License: mit
- Created: 2020-11-19T11:49:34.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2024-04-05T10:07:34.000Z (9 months ago)
- Last Synced: 2024-04-05T16:23:23.325Z (9 months ago)
- Topics: expo, react, react-native, tabs, tabview
- Language: TypeScript
- Homepage:
- Size: 80.9 MB
- Stars: 754
- Watchers: 10
- Forks: 153
- Open Issues: 135
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# React Native Collapsible Tab View
[![Build Status][build-badge]][build]
[![Version][version-badge]][package]
[![MIT License][license-badge]][license]
[![Runs with Expo][expo-badge]][expo]- [Expo App](#expo-app)
- [Demo](#demo)
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Guides](#guides)
- [Scroll on Header](#scroll-on-header)
- [API Reference](#api-reference)
- [Core](#core)
- [Tabs.Container](#tabscontainer)
- [Tabs.Lazy](#tabslazy)
- [Tabs.FlatList](#tabsflatlist)
- [Tabs.FlashList](#tabsflatlist)
- [Tabs.MasonryFlashList](#tabsmasonryflatlist)
- [Tabs.SectionList](#tabssectionlist)
- [Tabs.ScrollView](#tabsscrollview)
- [Ref](#ref)
- [Hooks](#hooks)
- [useCollapsibleStyle](#usecollapsiblestyle)
- [useAnimatedTabIndex](#useanimatedtabindex)
- [useFocusedTab](#usefocusedtab)
- [useHeaderMeasurements](#useheadermeasurements)
- [Default Tab Bar](#default-tab-bar)
- [MaterialTabBar](#materialtabbar)
- [MaterialTabItem](#materialtabitem)
- [Known Issues](#known-issues)
- [Android FlatList Pull to Refresh](#android-flatlist-pull-to-refresh)
- [iOS FlatList StickyHeaderIndices](#ios-flatlist-stickyheaderindices)
- [ref.setIndex](#refsetindex)
- [Alternative Libraries](#alternative-libraries)
- [Contributing](#contributing)
- [Documentation Changes](#documentation-changes)## 🚀 Version 6 released with Reanimated v3 support
React Native Collapsible Tab View is a versatile library for creating collapsible tab views using [Reanimated](https://github.com/software-mansion/react-native-reanimated).
- Explore the [examples](https://github.com/PedroBern/react-native-collapsible-tab-view/tree/main/example) for the source code of the Expo app.
**Credits**
The [react-native-tab-view](https://github.com/satya164/react-native-tab-view) example app was used as a template for the demos.
# Demo
| Default | Snap | revealHeaderOnScroll | revealHeaderOnScroll + Snap |
| :--------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: |
| | | | |# Features
- UI thread animations and interactions
- High customizability
- Full [TypeScript](https://typescriptlang.org) support
- Lazy loading with fade-in animation
- DiffClamp header
- Interpolated header
- Scroll snap (with interpolated header)
- Animated snap (with diffClamp header)
- Scrollable tabs, inspired by the [react-native-tab-view](https://github.com/satya164/react-native-tab-view) tab bar# Installation
To install the library, open a terminal in your project's root directory and run:
```sh
yarn add react-native-collapsible-tab-view react-native-pager-view
```Then, add [Reanimated](https://docs.swmansion.com/react-native-reanimated), [follow the official installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation).
# Quick Start
```tsx
import React from 'react'
import { View, StyleSheet, ListRenderItem } from 'react-native'
import { Tabs } from 'react-native-collapsible-tab-view'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 (
)
}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
### Tabs.Container
Basic usage looks like this:
```tsx
import { Tabs } from 'react-native-collapsible-tab-view'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, 'data' \| 'keyExtractor' \| 'renderItem' \| 'horizontal' \| 'pagingEnabled' \| 'onScroll' \| 'showsHorizontalScrollIndicator' \| 'getItemLayout'>`||Props passed to the pager. If you want for example to disable swiping, you can pass `{ scrollEnabled: false }`|
|`renderHeader`|`(props: TabBarProps) => React.ReactElement \| null`|||
|`renderTabBar`|`(props: TabBarProps) => React.ReactElement \| null`|`(props: TabBarProps) => MaterialTabBar`||
|`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.|### Tabs.Tab
Wrap your screens with `Tabs.Tab`. Basic usage looks like this:
```tsx
```
#### Props
|name|type|
|:----:|:----:|
|`label`|`string \| ((props: TabItemProps) => ReactNode) \| undefined`|
|`name`|`T`|### Tabs.Lazy
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`|### Tabs.FlatList
Use like a regular FlatList.
### Tabs.FlashList
Use like a regular FlashList.
### Tabs.MasonryFlashList
Use like a regular MasonryFlashList.
### Tabs.ScrollView
Use like a regular ScrollView.
### Tabs.SectionList
Use like a regular SectionList.
### Ref
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
### `useCollapsibleStyle`
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; }` |### `useAnimatedTabIndex`
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()
```### `useFocusedTab`
This hook returns the name of the currently focused tab.
```tsx
const focusedTab = useFocusedTab()
```### `useHeaderMeasurements`
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()
```### useCurrentTabScrollY
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
### MaterialTabItem
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) => ReactNode)`||
|`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](https://github.com/software-mansion/react-native-reanimated/issues/1703). We utilize [scrollTo](https://docs.swmansion.com/react-native-reanimated/docs/next/api/nativeMethods/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](https://github.com/PedroBern/react-native-collapsible-tab-view/issues/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](https://github.com/PedroBern/react-native-collapsible-segmented-view)
# Contributing and running the Example
While developing, you can run the [example app](/example/README.md) 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](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-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](https://github.com/PedroBern/react-native-collapsible-tab-view/tree/main/documentation/README_TEMPLATE.md), or update the docstrings inside the `src` folder, and run:
```sh
yarn docs
```[build-badge]: https://img.shields.io/circleci/build/github/PedroBern/react-native-collapsible-tab-view/main.svg?style=flat-square
[build]: https://app.circleci.com/pipelines/github/PedroBern/react-native-collapsible-tab-view
[version-badge]: https://img.shields.io/npm/v/react-native-collapsible-tab-view.svg?style=flat-square
[package]: https://www.npmjs.com/package/react-native-collapsible-tab-view
[license-badge]: https://img.shields.io/npm/l/react-native-collapsible-tab-view.svg?style=flat-square
[license]: https://opensource.org/licenses/MIT
[expo-badge]: https://img.shields.io/badge/Runs%20with%20Expo-4630EB.svg?style=flat-square&logo=EXPO&labelColor=f3f3f3&logoColor=000
[expo]: https://github.com/expo/expo