Skip to content

Refactor TabController to use Reanimated v2 API #1325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Jun 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0b8354b
WIP - Refactor TabController to use Reanimated v2 API
ethanshar May 31, 2021
8d554c2
Fix focus scroll and other animation issues
ethanshar Jun 2, 2021
822b7c8
Merge branch 'master' into feat/TabControllerV2
ethanshar Jun 8, 2021
bc850fc
Merge branch 'master' into feat/TabControllerV2
ethanshar Jun 10, 2021
9bca80d
Merge branch 'master' into feat/TabControllerV2
ethanshar Jun 20, 2021
5a176bc
Fix TS errors
ethanshar Jun 20, 2021
33800b5
Fix transition issue conflicted by page carousel scrolling
ethanshar Jun 20, 2021
684e9da
Fix onChangeIndex callback
ethanshar Jun 20, 2021
69ed843
remove font weight from selected label style
ethanshar Jun 20, 2021
513e7bf
Clean up
ethanshar Jun 20, 2021
f057cf1
cleanup code
ethanshar Jun 20, 2021
f25ef42
export TabController2 separately
ethanshar Jun 20, 2021
aa391a8
Rename loaded to shouldLoad to make it more readable
ethanshar Jun 21, 2021
9bc144f
Merge branch 'master' into feat/TabControllerV2
ethanshar Jun 22, 2021
e746854
Fix naming
ethanshar Jun 22, 2021
9a2b7ed
Fix exception in TabBarItem
ethanshar Jun 22, 2021
bc407b5
Fix issue with timing of lazy loading in TabPage
ethanshar Jun 22, 2021
78cb7ed
Fix TS error of PageCarousel ref
ethanshar Jun 22, 2021
920c268
Support missing features: spreadItems, indicatorInsets
ethanshar Jun 22, 2021
5f50259
Round value in the right place
ethanshar Jun 22, 2021
e3dd1b9
improve code
ethanshar Jun 22, 2021
8c12d49
Remove redundant fragment
ethanshar Jun 22, 2021
11d46d3
declare itemStates type
ethanshar Jun 22, 2021
07963c3
remove itemStates from context and cleanup code
ethanshar Jun 22, 2021
27ab03e
Fix support for passing custom style to TabBarItem
ethanshar Jun 22, 2021
a9e006a
Fix lint, include generated types
ethanshar Jun 22, 2021
36a9bcd
export tabController2 in index.ts as well
ethanshar Jun 23, 2021
2827307
Export TabController2 in generateTypes index
ethanshar Jun 23, 2021
2c53fa2
Fix reanimated exception with attempt to access stylesheet objects
ethanshar Jun 23, 2021
1fd262c
Merge branch 'master' into feat/TabControllerV2
ethanshar Jun 27, 2021
ff8c461
Pass labelStyle and selectedLabelStyle to expose the potential error …
ethanshar Jun 27, 2021
32bffa8
Add comments
ethanshar Jun 27, 2021
6bc523f
Fix typings
ethanshar Jun 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions demo/src/screens/componentScreens/TabControllerScreen/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {Component} from 'react';
import {ActivityIndicator} from 'react-native';
import {ActivityIndicator, StyleSheet} from 'react-native';
import {Assets, TabController, Colors, View, Text, Button, TabControllerItemProps} from 'react-native-ui-lib';
import {gestureHandlerRootHOC} from 'react-native-gesture-handler';
import _ from 'lodash';
Expand All @@ -20,7 +20,6 @@ interface State {
}

class TabControllerScreen extends Component<{}, State> {

constructor(props: {}) {
super(props);
this.state = {
Expand All @@ -36,12 +35,23 @@ class TabControllerScreen extends Component<{}, State> {
}

generateTabItems = (fewItems = this.state.fewItems): TabControllerItemProps[] => {
let items: TabControllerItemProps[] = _.chain(TABS)
const items: TabControllerItemProps[] = _.chain(TABS)
.take(fewItems ? 3 : TABS.length)
.map<TabControllerItemProps>(tab => ({label: tab, key: tab}))
.map<TabControllerItemProps>((tab, index) => ({
label: tab,
key: tab,
icon: index === 2 ? Assets.icons.demo.dashboard : undefined,
badge: index === 5 ? {label: '2'} : undefined
}))
.value();

const addItem: TabControllerItemProps = {icon: Assets.icons.demo.add, key: 'add', ignore: true, width: 60, onPress: this.onAddItem};
const addItem: TabControllerItemProps = {
icon: Assets.icons.demo.add,
key: 'add',
ignore: true,
width: 60,
onPress: this.onAddItem
};

return fewItems ? items : [...items, addItem];
};
Expand Down Expand Up @@ -93,7 +103,7 @@ class TabControllerScreen extends Component<{}, State> {
renderLoadingPage() {
return (
<View flex center>
<ActivityIndicator size="large" />
<ActivityIndicator size="large"/>
<Text text60L marginT-10>
Loading
</Text>
Expand All @@ -108,13 +118,13 @@ class TabControllerScreen extends Component<{}, State> {
return (
<Container {...containerProps}>
<TabController.TabPage index={0}>
<Tab1 />
<Tab1/>
</TabController.TabPage>
<TabController.TabPage index={1}>
<Tab2 />
<Tab2/>
</TabController.TabPage>
<TabController.TabPage index={2} lazy lazyLoadTime={1500} renderLoading={this.renderLoadingPage}>
<Tab3 />
<Tab3/>
</TabController.TabPage>

{_.map(_.takeRight(TABS, TABS.length - 3), (title, index) => {
Expand Down Expand Up @@ -147,10 +157,12 @@ class TabControllerScreen extends Component<{}, State> {
// uppercase
// indicatorStyle={{backgroundColor: 'green', height: 3}}
// indicatorInsets={0}
// spreadItems={false}
spreadItems={!fewItems}
backgroundColor={fewItems ? 'transparent' : undefined}
// labelColor={'green'}
// selectedLabelColor={'red'}
// labelStyle={{fontSize: 20}}
labelStyle={styles.labelStyle}
selectedLabelStyle={styles.selectedLabelStyle}
// iconColor={'green'}
// selectedIconColor={'blue'}
enableShadow
Expand Down Expand Up @@ -192,3 +204,12 @@ class TabControllerScreen extends Component<{}, State> {
}

export default gestureHandlerRootHOC(TabControllerScreen);

const styles = StyleSheet.create({
labelStyle: {
fontSize: 16
},
selectedLabelStyle: {
fontSize: 16
}
});
9 changes: 9 additions & 0 deletions generatedTypes/components/tabController2/FadedScrollView.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { ViewProps, ScrollViewProps } from 'react-native';
import { ForwardRefInjectedProps } from '../../commons/forwardRef';
export declare type FadedScrollViewProps = ViewProps & ScrollViewProps & {
children?: React.ReactNode | React.ReactNode[];
};
declare type Props = FadedScrollViewProps & ForwardRefInjectedProps;
declare const _default: React.ComponentType<Props>;
export default _default;
10 changes: 10 additions & 0 deletions generatedTypes/components/tabController2/PageCarousel.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// <reference types="react" />
/**
* @description: TabController's Page Carousel
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx
* @notes: You must pass `asCarousel` flag to TabController and render your TabPages inside a PageCarousel
*/
declare function PageCarousel({ ...props }: {
[x: string]: any;
}): JSX.Element;
export default PageCarousel;
90 changes: 90 additions & 0 deletions generatedTypes/components/tabController2/TabBar.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { StyleProp, ViewStyle } from 'react-native';
import { TabControllerItemProps } from './TabBarItem';
export interface TabControllerBarProps {
/**
* The list of tab bar items
*/
items?: TabControllerItemProps[];
/**
* Tab Bar height
*/
height?: number;
/**
* Show Tab Bar bottom shadow
*/
enableShadow?: boolean;
/**
* custom shadow style
*/
shadowStyle?: StyleProp<ViewStyle>;
/**
* custom style for the selected indicator
*/
indicatorStyle?: StyleProp<ViewStyle>;
/**
* custom label style
*/
labelStyle?: TabControllerItemProps['labelStyle'];
/**
* custom selected label style
*/
selectedLabelStyle?: TabControllerItemProps['selectedLabelStyle'];
/**
* the default label color
*/
labelColor?: string;
/**
* the selected label color
*/
selectedLabelColor?: string;
/**
* whether to change the text to uppercase
*/
uppercase?: boolean;
/**
* icon tint color
*/
iconColor?: string;
/**
* icon selected tint color
*/
selectedIconColor?: string;
/**
* TODO: rename to feedbackColor
* Apply background color on press for TouchableOpacity
*/
activeBackgroundColor?: string;
/**
* The TabBar background Color
*/
backgroundColor?: string;
/**
* The TabBar container width
*/
containerWidth?: number;
/**
* Pass to center selected item
*/
centerSelected?: boolean;
/**
* Whether the tabBar should be spread (default: true)
*/
spreadItems?: boolean;
/**
* The indicator insets (default: Spacings.s4, set to 0 to make it wide as the item)
*/
indicatorInsets?: number;
/**
* Additional styles for the container
*/
containerStyle?: StyleProp<ViewStyle>;
/**
* Used as a testing identifier
*/
testID?: string;
}
declare const _default: React.ComponentClass<TabControllerBarProps & {
useCustomTheme?: boolean | undefined;
}, any>;
export default _default;
16 changes: 16 additions & 0 deletions generatedTypes/components/tabController2/TabBarContext.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import Reanimated from 'react-native-reanimated';
interface TabControllerContext {
selectedIndex?: number;
items?: any[];
asCarousel?: boolean;
containerWidth: Reanimated.SharedValue<number>;
pageWidth?: number;
/** static page index */
currentPage: Reanimated.SharedValue<number>;
/** transition page index (can be a fraction when transitioning between pages) */
targetPage: Reanimated.SharedValue<number>;
carouselOffset: Reanimated.SharedValue<number>;
}
declare const TabBarContext: React.Context<TabControllerContext>;
export default TabBarContext;
88 changes: 88 additions & 0 deletions generatedTypes/components/tabController2/TabBarItem.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/// <reference types="react" />
import { TextStyle, LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';
import Reanimated from 'react-native-reanimated';
import { BadgeProps } from '../badge';
export interface TabControllerItemProps {
/**
* label of the tab
*/
label?: string;
/**
* custom label style
*/
labelStyle?: StyleProp<TextStyle>;
/**
* custom selected label style
*/
selectedLabelStyle?: StyleProp<TextStyle>;
/**
* the default label color
*/
labelColor?: string;
/**
* the selected label color
*/
selectedLabelColor?: string;
/**
* icon of the tab
*/
icon?: number;
/**
* icon tint color
*/
iconColor?: string;
/**
* icon selected tint color
*/
selectedIconColor?: string;
/**
* Badge component props to display next the item label
*/
badge?: BadgeProps;
/**
* A fixed width for the item
*/
width?: number;
/**
* ignore of the tab
*/
ignore?: boolean;
/**
* callback for when pressing a tab
*/
onPress?: (index: number) => void;
/**
* whether to change the text to uppercase
*/
uppercase?: boolean;
/**
* The active opacity when pressing a tab
*/
activeOpacity?: number;
/**
* TODO: rename to feedbackColor
* Apply background color on press for TouchableOpacity
*/
activeBackgroundColor?: string;
/**
* Pass custom style
*/
style?: StyleProp<ViewStyle>;
/**
* Used as a testing identifier
*/
testID?: string;
}
interface Props extends TabControllerItemProps {
index: number;
targetPage: any;
currentPage: Reanimated.Adaptable<number>;
onLayout?: (event: LayoutChangeEvent, index: number) => void;
}
/**
* @description: TabController's TabBarItem
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx
* @notes: Must be rendered as a direct child of TabController.TabBar.
*/
export default function TabBarItem({ index, label, labelColor, selectedLabelColor, labelStyle, selectedLabelStyle, icon, badge, uppercase, activeOpacity, activeBackgroundColor, testID, ignore, style, ...props }: Props): JSX.Element;
export {};
28 changes: 28 additions & 0 deletions generatedTypes/components/tabController2/TabPage.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PropsWithChildren } from 'react';
export interface TabControllerPageProps {
/**
* The index of the the TabPage
*/
index: number;
/**
* Whether this page should be loaded lazily
*/
lazy?: boolean;
/**
* How long to wait till lazy load complete (good for showing loader screens)
*/
lazyLoadTime?: number;
/**
* Render a custom loading page when lazy loading
*/
renderLoading?: () => JSX.Element;
/**
* Used as a testing identifier
*/
testID?: string;
}
/**
* @description: TabController's TabPage
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx
*/
export default function TabPage({ testID, index, lazy, renderLoading, ...props }: PropsWithChildren<TabControllerPageProps>): JSX.Element;
45 changes: 45 additions & 0 deletions generatedTypes/components/tabController2/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { PropsWithChildren } from 'react';
import { TabControllerItemProps } from './TabBarItem';
export { TabControllerItemProps };
export interface TabControllerProps {
/**
* The list of tab bar items
*/
items: TabControllerItemProps[];
/**
* Initial selected index
*/
selectedIndex: number;
/**
* callback for when index has change (will not be called on ignored items)
*/
onChangeIndex?: (index: number, prevIndex: number | null) => void;
/**
* When using TabController.PageCarousel this should be turned on
*/
asCarousel?: boolean;
/**
* Pass for custom carousel page width
*/
carouselPageWidth?: number;
}
/**
* @description: A performant solution for a tab controller with lazy load mechanism
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx
* @notes: This component is based on react-native-gesture-handler
* @important: On Android, if using react-native-navigation, make sure to wrap your screen with gestureHandlerRootHOC
* @importantLink: https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html#with-wix-react-native-navigation-https-githubcom-wix-react-native-navigation
*/
declare function TabController({ selectedIndex, asCarousel, items, onChangeIndex, carouselPageWidth, children }: PropsWithChildren<TabControllerProps>): JSX.Element | null;
declare namespace TabController {
var TabBar: React.ComponentClass<import("./TabBar").TabControllerBarProps & {
useCustomTheme?: boolean | undefined;
}, any>;
var TabBarItem: typeof import("./TabBarItem").default;
var TabPage: typeof import("./TabPage").default;
var PageCarousel: typeof import("./PageCarousel").default;
}
declare const _default: React.ComponentClass<TabControllerProps & {
useCustomTheme?: boolean | undefined;
}, any> & typeof TabController;
export default _default;
Loading