npm package: @maximedemurger/tab-view
Description: A react native component, support collapse header and custom refresh control, power by Reanimated v4 & GestureHandler V2.
Author: Maxime Demurger
Latest version: 0.3.3

# @maximedemurger/tab-view

<div align="center">
  <img alt="@maximedemurger/tab-view logo" src="./example/assets/icon.png" width="100" />
  
  A high-performance React Native tab view component with collapsible header and custom refresh control.
  
  Powered by [React Native Reanimated v4](https://docs.swmansion.com/react-native-reanimated/) and [GestureHandler v2](https://docs.swmansion.com/react-native-gesture-handler/).

  [![npm version](https://img.shields.io/npm/v/@maximedemurger/tab-view.svg?style=flat-square)](https://www.npmjs.com/package/@maximedemurger/tab-view)
  [![npm downloads](https://img.shields.io/npm/dm/@maximedemurger/tab-view.svg?style=flat-square)](https://www.npmjs.com/package/@maximedemurger/tab-view)
  [![license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)
</div>

---

## Features

- 🎯 **Smooth Collapsible Header** - Seamless header collapse/expand animations
- 🔄 **Custom Refresh Control** - Pull-to-refresh with customizable behavior
- ⚡ **High Performance** - Built with Reanimated v4 for 60fps animations
- 📱 **Cross-Platform** - Support for iOS, Android, and Web
- 🎪 **FlashList Support** - Optimized for high-performance lists
- 🪝 **React Hooks** - Modern API using React Hooks
- 📦 **TypeScript** - Full TypeScript support with type definitions
- 🌊 **Bounce Effect** - iOS bounce effect on pull-to-refresh
- 🧩 **Extensible** - Easy to customize and extend

## Requirements

Before installing this package, ensure you have:

- **React Native 0.76+**
- **Expo SDK 54+** (for Expo projects)
- **React 18.3+**

### Peer Dependencies

```json
{
  "react-native": ">=0.76",
  "react-native-gesture-handler": ">=2.0.0",
  "react-native-pager-view": ">=5.0.0",
  "react-native-reanimated": ">=4.0.0",
  "react-native-tab-view": ">3.3.0"
}
```

## Installation

### Step 1: Install the package

```bash
npm install @maximedemurger/tab-view
# or
yarn add @maximedemurger/tab-view
```

### Step 2: Install peer dependencies

If you don't have them already:

```bash
npm install react-native-gesture-handler react-native-pager-view react-native-reanimated react-native-tab-view
# or
yarn add react-native-gesture-handler react-native-pager-view react-native-reanimated react-native-tab-view
```

**For Expo projects:**
```bash
expo install react-native-gesture-handler react-native-pager-view react-native-reanimated react-native-tab-view
```

### Step 3: Setup (if needed)

For React Native projects, follow the setup instructions for:
- [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/docs/next/getting-started)
- [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/docs/getting-started)

## Quick Start

```tsx
import React, { useCallback, useState } from "react";
import { StatusBar, Text, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import { 
  TabView,
  Route,
  TabFlatList 
} from "@maximedemurger/tab-view";

export function Example() {
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [routes] = useState<Route[]>([
    { key: "tab1", title: "Feed", index: 0 },
    { key: "tab2", title: "Favorites", index: 1 },
    { key: "tab3", title: "Trending", index: 2 },
  ]);
  const [index, setIndex] = useState(0);
  
  const animationHeaderPosition = useSharedValue(0);
  const animationHeaderHeight = useSharedValue(0);

  const renderScene = useCallback(({ route }) => {
    switch (route.key) {
      case "tab1":
        return (
          <TabFlatList
            index={route.index}
            data={Array.from({ length: 20 })}
            estimatedItemSize={60}
            renderItem={({ index }) => (
              <View
                style={{
                  height: 60,
                  backgroundColor: "#fff",
                  marginBottom: 8,
                  justifyContent: "center",
                  paddingHorizontal: 16,
                }}
              >
                <Text>Feed Item {index + 1}</Text>
              </View>
            )}
          />
        );
      case "tab2":
      case "tab3":
        return (
          <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
            <Text>{route.title}</Text>
          </View>
        );
      default:
        return null;
    }
  }, []);

  const renderHeader = () => (
    <View
      style={{
        height: 300,
        backgroundColor: "#2196F3",
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <Text style={{ color: "#fff", fontSize: 24, fontWeight: "bold" }}>
        Header
      </Text>
    </View>
  );

  const onStartRefresh = async () => {
    setIsRefreshing(true);
    // Simulate network request
    await new Promise((resolve) => setTimeout(resolve, 1000));
    setIsRefreshing(false);
  };

  return (
    <TabView
      navigationState={{ index, routes }}
      renderScene={renderScene}
      onIndexChange={setIndex}
      renderScrollHeader={renderHeader}
      minHeaderHeight={44 + (StatusBar.currentHeight || 0)}
      onStartRefresh={onStartRefresh}
      isRefreshing={isRefreshing}
      animationHeaderPosition={animationHeaderPosition}
      animationHeaderHeight={animationHeaderHeight}
      lazy
    />
  );
}
```

## API

### TabView Component Props

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `navigationState` | `{ index: number; routes: Route[] }` | ✅ | Current tab state |
| `renderScene` | `(props: SceneRendererProps) => ReactElement` | ✅ | Render tab content |
| `onIndexChange` | `(index: number) => void` | ✅ | Called when tab changes |
| `renderScrollHeader` | `() => ReactElement` | ⭕ | Header component |
| `minHeaderHeight` | `number` | ⭕ | Minimum header height |
| `onStartRefresh` | `() => Promise<void>` | ⭕ | Called on pull-to-refresh |
| `isRefreshing` | `boolean` | ⭕ | Is currently refreshing |
| `animationHeaderPosition` | `Animated.SharedValue<number>` | ⭕ | Header position animation |
| `animationHeaderHeight` | `Animated.SharedValue<number>` | ⭕ | Header height animation |
| `lazy` | `boolean` | ⭕ | Lazy load tabs (default: false) |

### Scrollable Components

The package provides pre-built scrollable components:

- **`TabFlatList`** - For flat lists with high performance
- **`TabScrollView`** - For scroll views
- **`TabSectionList`** - For section lists

All support the same props as their React Native counterparts plus an `index` prop.

## Compatibility

### Version Support

- **Expo SDK:** 54+
- **React Native:** 0.76+
- **React:** 18.3+
- **Reanimated:** 4.0+
- **GestureHandler:** 2.0+

### Platform Support

- ✅ iOS
- ✅ Android
- ✅ Web (via react-native-web)

## Migration from v0.1.x

If you're upgrading from @showtime-xyz/tab-view v0.1.x, see [MIGRATION.md](MIGRATION.md) for detailed instructions.

## Examples

Check the [example](./example) directory for more examples:

- Basic tab view with header
- Custom refresh control
- FlashList integration
- Advanced animations

To run the example:

```bash
cd example
yarn install
yarn start
```

Then:
- iOS: `yarn ios`
- Android: `yarn android`
- Web: `yarn web`

## Performance Tips

1. **Use FlashList** for large lists - it's more performant than FlatList
2. **Memoize callbacks** with `useCallback` to prevent unnecessary re-renders
3. **Use `lazy` prop** to defer rendering of off-screen tabs
4. **Optimize images** and assets
5. **Consider `React.memo`** for complex scene components

## Troubleshooting

### Header not collapsing?
- Ensure `renderScrollHeader` is provided
- Check that `minHeaderHeight` is set correctly
- Verify `animationHeaderPosition` and `animationHeaderHeight` are shared values

### Refresh control not working?
- Implement `onStartRefresh` callback
- Set `isRefreshing` state properly
- Ensure refresh is enabled on your scrollable component

### Performance issues?
- Use `lazy` prop to defer tab rendering
- Replace FlatList with FlashList
- Profile with React DevTools

## Contributing

Contributions are welcome! Please feel free to open an issue or submit a pull request.

## License

MIT - See [LICENSE](LICENSE) for details

## Credits

Originally created by [Showtime](https://github.com/showtime-xyz/showtime-tab-view)

Maintained and updated for modern React Native by [Digital Edge Studio](https://github.com/digitaledgestudio)

---

<div align="center">
  Made with ❤️ for the React Native community
</div>