Skip to content

Commit 3403c0d

Browse files
authored
Fix/tab controller safe area and indicator width (#1923)
* TabController - support safe area * TabController - support constant indicatorWidth * Move getScreenWidth out of the component code
1 parent 3f36c7a commit 3403c0d

File tree

5 files changed

+45
-8
lines changed

5 files changed

+45
-8
lines changed

generatedTypes/src/components/tabController/TabBar.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export interface TabControllerBarProps {
7575
* The indicator insets (default: Spacings.s4, set to 0 to make it wide as the item)
7676
*/
7777
indicatorInsets?: number;
78+
/**
79+
* Send to get a constant width of the indicator (overrides indicatorInsets)
80+
*/
81+
indicatorWidth?: number;
7882
/**
7983
* Additional styles for the container
8084
*/

generatedTypes/src/components/tabController/index.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export interface TabControllerProps {
2626
* Pass for custom carousel page width
2727
*/
2828
carouselPageWidth?: number;
29+
/**
30+
* Send if a SafeView is used in the context of the TabController.
31+
*/
32+
useSafeArea?: boolean;
2933
}
3034
/**
3135
* @description: A performant solution for a tab controller with lazy load mechanism
@@ -34,7 +38,7 @@ export interface TabControllerProps {
3438
* @important: On Android, if using react-native-navigation, make sure to wrap your screen with gestureHandlerRootHOC
3539
* @importantLink: https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html#with-wix-react-native-navigation-https-githubcom-wix-react-native-navigation
3640
*/
37-
declare function TabController({ initialIndex, selectedIndex, asCarousel, items, onChangeIndex, carouselPageWidth, children }: PropsWithChildren<TabControllerProps>): JSX.Element;
41+
declare function TabController({ initialIndex, selectedIndex, asCarousel, items, onChangeIndex, carouselPageWidth, useSafeArea, children }: PropsWithChildren<TabControllerProps>): JSX.Element;
3842
declare namespace TabController {
3943
var TabBar: React.ComponentClass<import("./TabBar").TabControllerBarProps & {
4044
useCustomTheme?: boolean | undefined;

src/components/tabController/PageCarousel.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const FIX_RTL = Constants.isRTL && Constants.isAndroid;
1818
* @notes: You must pass `asCarousel` flag to TabController and render your TabPages inside a PageCarousel
1919
*/
2020
function PageCarousel(props: ScrollViewProps) {
21-
const {onMomentumScrollEnd, ...others} = props;
21+
const {onMomentumScrollEnd, style, ...others} = props;
2222
const carousel = useAnimatedRef<Reanimated.ScrollView>();
2323
const {
2424
itemsCount,
@@ -96,9 +96,14 @@ function PageCarousel(props: ScrollViewProps) {
9696
onMomentumScrollEnd?.(event);
9797
}, [onMomentumScrollEnd]);
9898

99+
const _style = useMemo(() => {
100+
return [{width: pageWidth}, style];
101+
}, [pageWidth, style]);
102+
99103
return (
100104
<Reanimated.ScrollView
101105
{...others}
106+
style={_style}
102107
ref={carousel}
103108
horizontal
104109
pagingEnabled

src/components/tabController/TabBar.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ export interface TabControllerBarProps {
101101
* The indicator insets (default: Spacings.s4, set to 0 to make it wide as the item)
102102
*/
103103
indicatorInsets?: number;
104+
/**
105+
* Send to get a constant width of the indicator (overrides indicatorInsets)
106+
*/
107+
indicatorWidth?: number;
104108
/**
105109
* Additional styles for the container
106110
*/
@@ -143,6 +147,7 @@ const TabBar = (props: Props) => {
143147
centerSelected,
144148
spreadItems,
145149
indicatorInsets = Spacings.s4,
150+
indicatorWidth,
146151
containerStyle,
147152
testID
148153
} = props;
@@ -230,16 +235,25 @@ const TabBar = (props: Props) => {
230235

231236
const _indicatorTransitionStyle = useAnimatedStyle(() => {
232237
const value = targetPage.value;
233-
const width = interpolate(value,
234-
itemsWidthsAnimated.value.map((_v: number, i: number) => i),
235-
itemsWidthsAnimated.value.map((v: number) => v - 2 * indicatorInsets));
238+
let width, marginHorizontal;
239+
if (indicatorWidth) {
240+
width = indicatorWidth;
241+
marginHorizontal = interpolate(value,
242+
itemsWidthsAnimated.value.map((_v: number, i: number) => i),
243+
itemsWidthsAnimated.value.map((v: number) => (v - indicatorWidth) / 2));
244+
} else {
245+
marginHorizontal = indicatorInsets;
246+
width = interpolate(value,
247+
itemsWidthsAnimated.value.map((_v: number, i: number) => i),
248+
itemsWidthsAnimated.value.map((v: number) => v - 2 * indicatorInsets));
249+
}
236250

237251
const left = interpolate(value,
238252
itemsOffsetsAnimated.value.map((_v: any, i: number) => i),
239253
itemsOffsetsAnimated.value);
240254

241255
return {
242-
marginHorizontal: indicatorInsets,
256+
marginHorizontal,
243257
width,
244258
left
245259
};

src/components/tabController/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,17 @@ export interface TabControllerProps {
3838
* Pass for custom carousel page width
3939
*/
4040
carouselPageWidth?: number;
41+
/**
42+
* Send if a SafeView is used in the context of the TabController.
43+
*/
44+
useSafeArea?: boolean;
4145
}
4246

47+
const getScreenWidth = (useSafeArea: boolean) => {
48+
const {left, right} = Constants.getSafeAreaInsets();
49+
return Constants.windowWidth - (useSafeArea && Constants.isIphoneX ? left + right : 0);
50+
};
51+
4352
/**
4453
* @description: A performant solution for a tab controller with lazy load mechanism
4554
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx
@@ -54,17 +63,18 @@ function TabController({
5463
items,
5564
onChangeIndex = _.noop,
5665
carouselPageWidth,
66+
useSafeArea = false,
5767
children
5868
}: PropsWithChildren<TabControllerProps>) {
59-
const [screenWidth, setScreenWidth] = useState<number>(Constants.windowWidth);
69+
const [screenWidth, setScreenWidth] = useState<number>(getScreenWidth(useSafeArea));
6070

6171
if (items?.length < 2) {
6272
console.warn('TabController component expect a minimum of 2 items');
6373
}
6474

6575
useOrientation({
6676
onOrientationChange: () => {
67-
setScreenWidth(Constants.windowWidth);
77+
setScreenWidth(getScreenWidth(useSafeArea));
6878
}
6979
});
7080

0 commit comments

Comments
 (0)