Skip to content

Commit

Permalink
fix: pass isGliding ref instead of current value and update types
Browse files Browse the repository at this point in the history
BREAKING CHANGE: pass isGliding ref instead of isGliding.current

Migration: use isGliding.current instead of isGliding inside your
custom renderTabBar function


Fixes #33
  • Loading branch information
PedroBern committed Jan 5, 2021
1 parent 1f726b7 commit 37bf1b8
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 15 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ All props are optional, but if you are not rendering a header, you'd be probably
| `headerContainerStyle?` | Styles applied to header and tabbar container. | `undefined` |
| `preventTabPressOnGliding?` | Prevent tab press if screen is gliding. Ignored if `renderTabBar` is provided. | `true` |
| `disableSnap?` | Disable the snap animation. | `false` |
| `renderTabBar?` | Same as [renderTabBar](https://github.com/satya164/react-native-tab-view#rendertabbar) of the original [TabView](https://github.com/satya164/react-native-tab-view#tabview), but with the additional `isGliding` property. | `undefined` |
| `renderTabBar?` | Same as [renderTabBar](https://github.com/satya164/react-native-tab-view#rendertabbar) of the original [TabView](https://github.com/satya164/react-native-tab-view#tabview), but with the additional `isGliding` and `preventTabPressOnGliding` properties. | `undefined` |
| `snapThreshold?` | Percentage of header height to make the snap effect. A number between 0 and 1. | `0.5` |
| `snapTimeout?` | How long to wait before initiating the snap effect, in milliseconds. | `250` |
| `onHeaderHeightChange?` | Callback fired when the `headerHeight` state value inside `CollapsibleTabView` will be updated in the `onLayout` event from the tab/header container.<br/><br/> Useful to call layout animations. Example:<br/><br/><pre lang="js">() => {LayoutAnimation.configureNext(preset)};</pre> | `undefined` |
Expand Down
2 changes: 2 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import CollapsibleTabViewSmallContentExample from './CollapsibleTabViewSmallCont
import CollapsibleTabViewNoSnapExample from './CollapsibleTabViewNoSnapExample';
import CollapsibleTabViewResizeExample from './CollapsibleTabViewResizeExample';
import CollapsibleTabViewDemoExample from './CollapsibleTabViewDemoExample';
import CollapsibleTabViewCustomTabBarExample from './CollapsibleTabViewCustomTabBarExample';
import CollapsibleTabViewCenteredEmptyListExample from './CollapsibleTabViewCenteredEmptyListExample';
import MaterialTopTabsCollapsibleTabViewDemoExample from './NavigationExample';
import CollapsibleTabViewNoUpfrontHeightExample from './CollapsibleTabViewNoUpfrontHeightExample';
Expand All @@ -42,6 +43,7 @@ const EXAMPLE_COMPONENTS: ExampleComponentType[] = [
CollapsibleTabViewNoSnapExample,
CollapsibleTabViewResizeExample,
CollapsibleTabViewDemoExample,
CollapsibleTabViewCustomTabBarExample,
CollapsibleTabViewSmallContentExample,
CollapsibleTabViewNoUpfrontHeightExample,
MaterialTopTabsCollapsibleTabViewDemoExample,
Expand Down
128 changes: 128 additions & 0 deletions example/src/CollapsibleTabViewCustomTabBarExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import * as React from 'react';
import { StyleSheet, View, Text, Animated } from 'react-native';
import {
CollapsibleTabView,
RenderTabBarProps,
useCollapsibleScene,
} from 'react-native-collapsible-tab-view';
import { SceneMap, TabBarProps, TabBar } from 'react-native-tab-view';
import { ExampleComponentType } from './types';

type Route = {
key: string;
title: string;
icon?: string;
};

const SomeRoute: React.FC<{ routeKey: string; color: string }> = ({
routeKey,
color,
}) => {
const scrollPropsAndRef = useCollapsibleScene(routeKey);

return (
<Animated.ScrollView
style={{ backgroundColor: color }}
{...scrollPropsAndRef}
>
<View style={styles.content} />
</Animated.ScrollView>
);
};

const FirstScene = () => <SomeRoute routeKey="first" color="white" />;
const SecondScene = () => <SomeRoute routeKey="second" color="black" />;

const HEADER_HEIGHT = 250;

const renderHeader = () => (
<View style={styles.header}>
<Text style={styles.headerText}>COLLAPSIBLE</Text>
</View>
);

const renderScene = SceneMap({
first: FirstScene,
second: SecondScene,
});

const renderTabBar = (
props: RenderTabBarProps<Route, TabBarProps<Route>>
): React.ReactNode => (
<TabBar
{...props}
activeColor="pink"
inactiveColor="red"
onTabPress={(event) => {
if (props?.isGliding?.current && props?.preventTabPressOnGliding) {
event.preventDefault();
}
props?.onTabPress && props.onTabPress(event);
}}
renderLabel={({ color, focused, route }) => (
<View style={styles.label}>
<Text
style={[
styles.title,
{ color },
{ fontWeight: focused ? 'bold' : 'normal' },
]}
>
{route.icon ?? route.title}
</Text>
</View>
)}
/>
);

const CollapsibleTabViewExample: ExampleComponentType = () => {
const [index, setIndex] = React.useState(0);
const [routes] = React.useState<Route[]>([
{ key: 'first', title: 'First', icon: '👋' }, // or FontAwesome/badge
{ key: 'second', title: 'Second', icon: '🤯' },
]);

const handleIndexChange = (index: number) => {
setIndex(index);
};

return (
<CollapsibleTabView<Route, TabBarProps<Route>>
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={handleIndexChange}
renderHeader={renderHeader} // optional
renderTabBar={renderTabBar} // optional
headerHeight={HEADER_HEIGHT} // optional, will be computed.
/>
);
};

const styles = StyleSheet.create({
header: {
height: HEADER_HEIGHT,
backgroundColor: '#2196f3',
justifyContent: 'center',
alignItems: 'center',
elevation: 4,
},
headerText: {
color: 'white',
fontSize: 24,
},
content: {
height: 1500,
},
label: {
alignContent: 'center',
flex: 1,
justifyContent: 'center',
},
title: {},
});

CollapsibleTabViewExample.title = 'Custom Tab Bar demo';
CollapsibleTabViewExample.backgroundColor = '#2196f3';
CollapsibleTabViewExample.appbarElevation = 0;

export default CollapsibleTabViewExample;
39 changes: 26 additions & 13 deletions src/CollapsibleTabView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,19 @@ import { CollapsibleContextProvider } from './CollapsibleTabViewContext';
import scrollScene from './scrollScene';
import type { ScrollRef, GetRef } from './types';

export type Props<T extends Route> = Partial<TabViewProps<T>> &
type PTabBarProps<T extends Route> = Partial<TabBarProps<T>>;

export type RenderTabBarProps<T extends Route, P extends object = {}> = {
navigationState: NavigationState<T>;
isGliding: React.MutableRefObject<boolean>;
preventTabPressOnGliding: boolean;
} & SceneRendererProps &
P;

export type Props<
T extends Route,
P extends object = PTabBarProps<T>
> = Partial<Omit<TabViewProps<T>, 'renderTabBar'>> &
Pick<TabViewProps<T>, 'onIndexChange' | 'navigationState' | 'renderScene'> & {
/**
* Optionally controlled animated value.
Expand All @@ -38,9 +50,9 @@ export type Props<T extends Route> = Partial<TabViewProps<T>> &
/**
* Props passed to the tab bar component.
*/
tabBarProps?: Partial<TabBarProps<T>>;
tabBarProps?: P;
/**
* Header rendered on top of the tab bar. Defaul is `() => null`
* Header rendered on top of the tab bar. Default is `() => null`
*/
renderHeader?: () => React.ReactNode;
/**
Expand All @@ -57,14 +69,9 @@ export type Props<T extends Route> = Partial<TabViewProps<T>> &
disableSnap?: boolean;
/**
* Same as `renderTab` of `TabViewProps`, but with the additional
* `isGliding` property.
* `isGliding` and `preventTabPressOnGliding` properties.
*/
renderTabBar?: (
props: SceneRendererProps & {
navigationState: NavigationState<T>;
isGliding: boolean;
}
) => React.ReactNode;
renderTabBar?: (props: RenderTabBarProps<T, P>) => React.ReactNode;
/**
* Callback fired when the `headerHeight` state value inside
* `CollapsibleTabView` will be updated in the `onLayout` event
Expand Down Expand Up @@ -99,7 +106,10 @@ export type Props<T extends Route> = Partial<TabViewProps<T>> &
* `CollapsibleTabView` wraps the `TabView` and take care of animations /
* scroll value computations. It should be used with `useCollapsibleScene`.
*/
const CollapsibleTabView = <T extends Route>({
const CollapsibleTabView = <
T extends Route,
P extends object = PTabBarProps<T>
>({
animatedValue = new Animated.Value(0),
navigationState: { index, routes },
renderHeader = () => null,
Expand All @@ -115,7 +125,7 @@ const CollapsibleTabView = <T extends Route>({
snapTimeout = 250,
routeKeyProp = 'key',
...tabViewProps
}: React.PropsWithoutRef<Props<T>>): React.ReactElement => {
}: React.PropsWithoutRef<Props<T, P>>): React.ReactElement => {
const [headerHeight, setHeaderHeight] = React.useState(
Math.max(initialHeaderHeight, 0)
);
Expand Down Expand Up @@ -363,10 +373,12 @@ const CollapsibleTabView = <T extends Route>({
>
{renderHeader()}
{customRenderTabBar ? (
// @ts-ignore
customRenderTabBar({
...props,
...tabBarProps,
isGliding: isGliding.current,
isGliding,
preventTabPressOnGliding,
})
) : (
<TabBar
Expand All @@ -376,6 +388,7 @@ const CollapsibleTabView = <T extends Route>({
if (isGliding.current && preventTabPressOnGliding) {
event.preventDefault();
}
// @ts-ignore
tabBarProps?.onTabPress && tabBarProps.onTabPress(event);
}}
/>
Expand Down
5 changes: 4 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export { default as CollapsibleTabView } from './CollapsibleTabView';
export type { Props as CollapsibleTabViewProps } from './CollapsibleTabView';
export type {
Props as CollapsibleTabViewProps,
RenderTabBarProps,
} from './CollapsibleTabView';

export { default as useCollapsibleScene } from './useCollapsibleScene';
export type { CollapsibleScenePropsAndRef } from './types';
Expand Down

0 comments on commit 37bf1b8

Please sign in to comment.