satya164 / react-native-tab-view

A cross-platform Tab View component for React Native
MIT License
5.14k stars 1.07k forks source link

Can we support a sticky tab bar to support render a header component for tab view #1355

Closed jiyuan12354 closed 2 years ago

jiyuan12354 commented 2 years ago

Description

I want to hide the header component while scroll the tab screen, but the tab bar need be sticky on the top area. I believe there has many developers watching on this feature, because FlatList & ScrollView & SectionList all support a props stickyHeaderIndices to make specific sub component sticky. But unfortunately, our Tab View here is not support

Alternatives

but I have a customized react-native-tab-view+2.16.0.patch for react-native-tab-view@2.16.0, it may be inelegant but useful for myself

diff --git a/node_modules/react-native-tab-view/lib/module/TabBar.js b/node_modules/react-native-tab-view/lib/module/TabBar.js
index b287a8a..6199247 100644
--- a/node_modules/react-native-tab-view/lib/module/TabBar.js
+++ b/node_modules/react-native-tab-view/lib/module/TabBar.js
@@ -10,6 +10,7 @@ import Animated from 'react-native-reanimated';
 import TabBarItem from './TabBarItem';
 import TabBarIndicator from './TabBarIndicator';
 import memoize from './memoize';
+import { equals } from 'ramda'

 const scheduleInNextFrame = cb => {
   let frame = requestAnimationFrame(() => {
@@ -241,7 +242,7 @@ export default class TabBar extends React.Component {
     const translateX = this.getTranslateX(this.scrollAmount, this.getMaxScrollDistance(tabBarWidth, layout.width));
     return /*#__PURE__*/React.createElement(Animated.View, {
       onLayout: this.handleLayout,
-      style: [styles.tabBar, style]
+      style: style ?? styles.tabBar,
     }, /*#__PURE__*/React.createElement(Animated.View, {
       pointerEvents: "none",
       style: [styles.indicatorContainer, scrollEnabled ? {
@@ -307,7 +308,7 @@ export default class TabBar extends React.Component {
           this.measuredTabWidths[route.key] = e.nativeEvent.layout.width; // When we have measured widths for all of the tabs, we should updates the state
           // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app

-          if (routes.every(r => typeof this.measuredTabWidths[r.key] === 'number')) {
+          if (routes.every(r => typeof this.measuredTabWidths[r.key] === 'number') && Object.keys(this.measuredTabWidths).length === routes.length && !equals(this.state.tabWidths, this.measuredTabWidths)) {
             this.setState({
               tabWidths: _objectSpread({}, this.measuredTabWidths)
             });
diff --git a/node_modules/react-native-tab-view/lib/module/TabView.js b/node_modules/react-native-tab-view/lib/module/TabView.js
index d0041ce..fce3358 100644
--- a/node_modules/react-native-tab-view/lib/module/TabView.js
+++ b/node_modules/react-native-tab-view/lib/module/TabView.js
@@ -7,7 +7,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
 function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

 import * as React from 'react';
-import { StyleSheet, View } from 'react-native';
+import { StyleSheet, View, ScrollView } from 'react-native';
 import { GestureHandlerRootView } from 'react-native-gesture-handler';
 import Animated from 'react-native-reanimated';
 import TabBar from './TabBar';
@@ -72,7 +72,8 @@ export default class TabView extends React.Component {
       style,
       gestureHandlerProps,
       springVelocityScale,
-      renderPager
+      renderPager,
+      renderHeader = () => /*#__PURE__*/React.createElement(React.Fragment, null)
     } = this.props;
     const {
       layout
@@ -110,7 +111,7 @@ export default class TabView extends React.Component {
         };
         return /*#__PURE__*/React.createElement(React.Fragment, null, positionListener ? /*#__PURE__*/React.createElement(Animated.Code, {
           exec: Animated.set(positionListener, position)
-        }) : null, tabBarPosition === 'top' && renderTabBar(_objectSpread(_objectSpread({}, sceneRendererProps), {}, {
+        }) : null, /*#__PURE__*/React.createElement(ScrollView, {stickyHeaderIndices: [1]}, renderHeader({ ...sceneRendererProps, navigationState }), tabBarPosition === 'top' && renderTabBar(_objectSpread(_objectSpread({}, sceneRendererProps), {}, {
           navigationState
         })), render(navigationState.routes.map((route, i) => {
           return /*#__PURE__*/React.createElement(SceneView, _extends({}, sceneRendererProps, {
@@ -133,7 +134,7 @@ export default class TabView extends React.Component {
           })));
         })), tabBarPosition === 'bottom' && renderTabBar(_objectSpread(_objectSpread({}, sceneRendererProps), {}, {
           navigationState
-        })));
+        }))));
       }
     }));
   }
@@ -152,7 +153,7 @@ _defineProperty(TabView, "defaultProps", {
   springConfig: {},
   timingConfig: {},
   gestureHandlerProps: {},
-  renderPager: props => /*#__PURE__*/React.createElement(Pager, props)
+  renderPager: props => /*#__PURE__*/React.createElement(Pager, props),
 });

 const styles = StyleSheet.create({
diff --git a/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts b/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts
index a6cd166..356e4e8 100644
--- a/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts
+++ b/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts
@@ -49,4 +49,10 @@ export declare type PagerCommonProps = {
     timingConfig: {
         duration?: number;
     };
+
+  renderHeader?: (
+    props: SceneRendererProps & {
+      navigationState: NavigationState<Route>;
+    }
+  ) => React.ReactNode;
 };
diff --git a/node_modules/react-native-tab-view/src/TabBar.tsx b/node_modules/react-native-tab-view/src/TabBar.tsx
index 8f5d257..afb20e9 100644
--- a/node_modules/react-native-tab-view/src/TabBar.tsx
+++ b/node_modules/react-native-tab-view/src/TabBar.tsx
@@ -361,7 +361,7 @@ export default class TabBar<T extends Route> extends React.Component<
     return (
       <Animated.View
         onLayout={this.handleLayout}
-        style={[styles.tabBar, style]}
+        style={style ?? styles.tabBar}
       >
         <Animated.View
           pointerEvents="none"
@@ -500,7 +500,7 @@ const styles = StyleSheet.create({
     overflow: Platform.select({ default: 'scroll', web: undefined }),
   },
   tabBar: {
-    backgroundColor: '#2196f3',
+    backgroundColor: 'transparent',
     elevation: 4,
     shadowColor: 'black',
     shadowOpacity: 0.1,
diff --git a/node_modules/react-native-tab-view/src/TabView.tsx b/node_modules/react-native-tab-view/src/TabView.tsx
index fe8cf9b..79f42dd 100644
--- a/node_modules/react-native-tab-view/src/TabView.tsx
+++ b/node_modules/react-native-tab-view/src/TabView.tsx
@@ -5,6 +5,7 @@ import {
   StyleProp,
   ViewStyle,
   LayoutChangeEvent,
+  ScrollView,
 } from 'react-native';
 import {
   PanGestureHandler,
@@ -46,6 +47,11 @@ export type Props<T extends Route> = PagerCommonProps & {
   style?: StyleProp<ViewStyle>;
   gestureHandlerProps: React.ComponentProps<typeof PanGestureHandler>;
   renderPager: (props: ChildProps<T>) => React.ReactNode;
+  renderHeader?: (
+    props: SceneRendererProps & {
+      navigationState: NavigationState<T>;
+    }
+  ) => React.ReactNode;
 };

 type State = {
@@ -126,6 +132,7 @@ export default class TabView<T extends Route> extends React.Component<
       gestureHandlerProps,
       springVelocityScale,
       renderPager,
+      renderHeader = () => <></>,
     } = this.props;
     const { layout } = this.state;

@@ -170,6 +177,11 @@ export default class TabView<T extends Route> extends React.Component<
                     exec={Animated.set(positionListener, position)}
                   />
                 ) : null}
+                <ScrollView stickyHeaderIndices={[1]}>
+                {renderHeader({
+                    ...sceneRendererProps,
+                    navigationState,
+                  })}
                 {tabBarPosition === 'top' &&
                   renderTabBar({
                     ...sceneRendererProps,
@@ -208,6 +220,8 @@ export default class TabView<T extends Route> extends React.Component<
                     ...sceneRendererProps,
                     navigationState,
                   })}
+
+                </ScrollView>
               </React.Fragment>
             );
           },

Inspirations & examples

No response

github-actions[bot] commented 2 years ago

Couldn't find version numbers for the following packages in the issue:

Can you update the issue to include version numbers for those packages? The version numbers must match the format 1.2.3.

github-actions[bot] commented 2 years ago

Hey! Thanks for opening the issue. The issue doesn't seem to contain a link to a repro (a snack.expo.dev link or link to a GitHub repo under your username).

Can you provide a minimal repro which demonstrates the issue? A repro will help us debug the issue faster. Please try to keep the repro as small as possible and make sure that we can run it without additional setup.

lucianobracco-geojam commented 2 years ago

This would be awesome to have. On a current project this functionality would be so useful. Do you have any updates/plans for this?

BTW, thanks for the work you did. It's awesome!

jiyuan12354 commented 2 years ago

finally, I integrated react-native-collapsible-tab-view@2.0.2 with react-native-tab-view@2.16.0

github-actions[bot] commented 2 years ago

Hello 👋, this issue has been open for more than a month without a repro or any activity. If the issue is still present in the latest version, please provide a repro or leave a comment within 7 days to keep it open, otherwise it will be closed automatically. If you found a solution or workaround for the issue, please comment here for others to find. If this issue is critical for you, please consider sending a pull request to fix it.

KrisLau commented 2 years ago

finally, I integrated react-native-collapsible-tab-view@2.0.2 with react-native-tab-view@2.16.0

@jiyuan12354 Is this in a pull request?

okwasniewski commented 2 years ago

Closing this, since it's supported in react-native-collapsible-tab-view