/react-navigation-collapsible

An extension of react-navigation that makes your header collapsible.

Primary LanguageTypeScriptMIT LicenseMIT

react-navigation-collapsible

npm npm code style: prettier ci: github runs with expo

An extension of react-navigation that makes your header collapsible.

Try out on Expo Snack

Compatibility 🚧

react-navigation react-navigation-collapsible Documentation
≥ v5 (latest) v5 (latest) current
≥ v3 v3 v3-4 branch
v2 v2 v2 branch

🏗 The Collapsible Tab-navigator is no longer supported due to the Android bug from react-native.

Usage

1-1. Default Header

import {
  createCollapsibleStack,
  // disableExpoTranslucentStatusBar,
} from 'react-navigation-collapsible';

/* Expo only: If you disabled Expo's default translucent statusBar, please call this function as well.
disableExpoTranslucentStatusBar();
*/

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        /* Wrap your Stack.Screen */
        {createCollapsibleStack(
          <Stack.Screen
            name="HomeScreen"
            component={MyScreen}
            options={{
              headerStyle: { backgroundColor: 'green' },
              title: 'Home',
            }}
          />,
          {
            collapsedColor: 'red' /* Optional */,
            useNativeDriver: true /* Optional, default: true */,
            key:
              'HomeScreen' /* Optional, a key for your Stack.Screen element */,
            elevation: 4 /* Optional */,
          }
        )}
      </Stack.Navigator>
    </NavigationContainer>
  );
}
import { Animated } from 'react-native';
import { useCollapsibleStack } from 'react-navigation-collapsible';

const MyScreen = ({ navigation, route }) => {
  const {
    onScroll /* Event handler */,
    onScrollWithListener /* Event handler creator */,
    containerPaddingTop /* number */,
    scrollIndicatorInsetTop /* number */,
    /* Animated.AnimatedInterpolation by scrolling */
    translateY /* 0.0 ~ -headerHeight */,
    progress /* 0.0 ~ 1.0 */,
    opacity /* 1.0 ~ 0.0 */,
  } = useCollapsibleStack();

  /* in case you want to use your listener
  const listener = ({nativeEvent}) => {
    console.log(nativeEvent);
  };
  const onScroll = onScrollWithListener(listener);
  */

  return (
    <Animated.FlatList
      onScroll={onScroll}
      contentContainerStyle={{ paddingTop: containerPaddingTop }}
      scrollIndicatorInsets={{ top: scrollIndicatorInsetTop }}
      /* rest of your stuff */
    />
  );
};

See /example/App.tsx and /example/src/DefaultHeaderScreen.tsx

1-2. Sticky Header

See /example/src/StickyHeaderScreen.tsx


2. Sub Header (e.g Search Bar)

import { createCollapsibleStackSub } from 'react-navigation-collapsible';
/* use 'createCollapsibleStackSub' instead of 'createCollapsibleStack' */

/* The rest are the same with the default header. */
import { Animated } from 'react-native';
import {
  useCollapsibleStack,
  CollapsibleStackSub,
} from 'react-navigation-collapsible';

const MySearchBar = () => (
  <View style={{ padding: 15, width: '100%', height: 60 }}>
    <TextInput placeholder="search here" />
  </View>
);

const MyScreen = ({ navigation, route }) => {
  const {
    onScroll /* Event handler */,
    containerPaddingTop /* number */,
    scrollIndicatorInsetTop /* number */,
  } = useCollapsibleStack();

  return (
    <>
      <Animated.FlatList
        onScroll={onScroll}
        contentContainerStyle={{ paddingTop: containerPaddingTop }}
        scrollIndicatorInsets={{ top: scrollIndicatorInsetTop }}
        /* rest of your stuff */
      />
      /* Wrap your component with `CollapsibleStackSub` */
      <CollapsibleStackSub>
        <MySearchBar />
      </CollapsibleStackSub>
    </>
  );
};

See /example/App.tsx and /example/src/SubHeaderScreen.tsx

3. Custom Header

Custom Header implementation example

function App() {
  return (
    <NavigationContainer
      /* Add headerMode="screen" to prevent the custom header from clashing with subsequent headers.
         If you don't do this, you will have to make sure the header is applied consistently.
         You can check the Custom Header implementation example to see a possible configuration for this */
      headerMode="screen"
    >
      <Stack.Navigator>
        /* Wrap your Stack.Screen */
        {createCollapsibleStack(
          <Stack.Screen
            name="HomeScreen"
            component={MyScreen}
            options={{
              title: 'Home',
            }}
          />,
          {
            /* Add a custom header to the createCollapsibleStack options the same way
               you would add it to the Stack.Screen options */
            header: ({ scene, previous, navigation }) => {
              const { options } = scene.descriptor;
              const title =
                options.headerTitle !== undefined
                  ? options.headerTitle
                  : options.title !== undefined
                  ? options.title
                  : scene.route.name;

              return (
                <MyHeader
                  title={title}
                  leftButton={
                    previous ? <MyBackButton onPress={navigation.goBack} /> : undefined
                  }
                  style={options.headerStyle}
                />
              );
            };
          }
        )}
      </Stack.Navigator>
    </NavigationContainer>
  );
}

See /example/App.tsx and /example/src/CustomHeaderScreen.tsx

Install

# install module
yarn add react-navigation-collapsible

Contribution

PR is welcome!

Testing your library code with the example

/example imports the library directly from the root folder, configured with babel-plugin-module-resolver. So, just turn the watch option on at the root folder while you're making changes on the library, and check them on the example.

yarn tsc -w