react-native-paper-tabs
Version:
Smooth and fast cross platform Material Design Tabs for React Native Paper
238 lines (234 loc) • 7.13 kB
JavaScript
"use strict";
import { Animated, Platform, ScrollView, StyleSheet, View } from 'react-native';
import { overlay, Surface } from 'react-native-paper';
import color from 'color';
import * as React from 'react';
import { useIndicator, useOffsetScroller } from './internal';
import TabsHeaderItem from "./TabsHeaderItem.js";
import { TabsContext } from "./context.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export default function TabsHeader({
position,
offset,
theme,
dark,
style,
iconPosition,
showTextLabel,
showLeadingSpace,
uppercase,
mode,
tabHeaderStyle,
tabLabelStyle,
children
}) {
const {
index,
goTo
} = React.useContext(TabsContext);
const {
colors,
dark: isDarkTheme,
mode: themeMode,
isV3
} = theme;
const {
backgroundColor: customBackground,
elevation: _elevation,
...restStyle
} = StyleSheet.flatten(style) || {};
let elevation = theme.isV3 ? _elevation : _elevation || 4;
let isDark;
const backgroundColorV2 = isDarkTheme && themeMode === 'adaptive' ? overlay(elevation || 0, colors.surface) : colors.primary;
const backgroundColorV3 = theme.colors.surface;
const backgroundColor = customBackground ? customBackground : isV3 ? backgroundColorV3 : backgroundColorV2;
let hasPrimaryBackground = colors.primary === backgroundColor;
if (typeof dark === 'boolean') {
isDark = dark;
} else {
isDark = backgroundColor === 'transparent' ? false :
// @ts-ignore
!color(backgroundColor).isLight();
}
const textColorV2 = isDark ? '#fff' : '#000';
const activeColorV2 = hasPrimaryBackground ? textColorV2 : theme.colors.primary;
// Color (active) On surface md.sys.color.on-surface
// Color (inactive) On surface variant md.sys.color.on-surface-variant
const textColorV3 = colors.onSurfaceVariant;
const activeColorV3 = colors.onSurface;
const textColor = isV3 ? textColorV3 : textColorV2;
const activeColor = isV3 ? activeColorV3 : activeColorV2;
const innerScrollSize = React.useRef(null);
const scrollX = React.useRef(0);
const scrollRef = React.useRef(null);
const layouts = React.useRef(null);
const [tabsLayout, setTabsLayout] = React.useState(null);
const [indicatorRef, onUpdateTabLayout, indicatorStyle] = useIndicator({
tabsLayout,
layouts,
index,
offset,
position,
childrenCount: children.length
});
const onTabsLayout = React.useCallback(e => {
setTabsLayout(e.nativeEvent.layout);
}, [setTabsLayout]);
const onTabLayout = React.useCallback((tabIndex, event) => {
layouts.current = {
...layouts.current,
[tabIndex]: event.nativeEvent.layout
};
onUpdateTabLayout();
}, [layouts, onUpdateTabLayout]);
const updateScroll = React.useCallback(scrollType => {
if (!layouts.current) {
console.log('returning no layout');
return;
}
let cl = layouts.current[index];
if (!cl || !scrollRef.current || !tabsLayout) {
console.log('!cl || !scrollRef.current || !tabsLayout');
return;
}
const tabsWidth = tabsLayout.width;
let scrolledX = scrollX.current;
if (scrollType === 'next') {
const next = layouts.current?.[index + 1];
if (next) {
cl = next;
}
} else if (scrollType === 'prev') {
const prev = layouts.current?.[index - 1];
if (prev) {
cl = prev;
}
}
const relativeX = cl.x - scrolledX;
const overflowLeft = relativeX;
const overflowRight = -tabsWidth + relativeX + cl.width;
if (overflowRight > -50) {
scrollRef.current.scrollTo({
x: scrolledX + overflowRight + 50,
y: 0,
animated: true
});
} else if (overflowLeft < 50) {
scrollRef.current.scrollTo({
x: scrolledX + overflowLeft - 50,
y: 0,
animated: true
});
}
}, [layouts, index, scrollRef, scrollX, tabsLayout]);
// subscribes to offset on native devices to scroll tab bar faster when scrolling (iOS only since Android bugs)
useOffsetScroller({
updateScroll,
index,
offset,
mode
});
// updates scroll when index changes (updateScroll function changes to new reference when index changes)
React.useEffect(() => {
updateScroll();
}, [updateScroll]);
const SurfaceComponent = theme.isV3 ? View : Surface;
return /*#__PURE__*/_jsx(Animated.View, {
style: [styles.relative, tabHeaderStyle],
children: /*#__PURE__*/_jsxs(SurfaceComponent, {
style: [{
backgroundColor,
elevation
}, restStyle, styles.tabs, iconPosition === 'top' && styles.tabsTopIcon],
onLayout: onTabsLayout,
children: [/*#__PURE__*/_jsxs(ScrollView, {
ref: scrollRef,
contentContainerStyle: mode === 'fixed' ? styles.fixedContentContainerStyle : undefined,
onContentSizeChange: e => {
innerScrollSize.current = e;
},
onScroll: e => {
scrollX.current = e.nativeEvent.contentOffset.x;
},
scrollEventThrottle: 25,
horizontal: true,
showsHorizontalScrollIndicator: false,
alwaysBounceHorizontal: mode === 'scrollable',
scrollEnabled: mode === 'scrollable',
children: [mode === 'scrollable' && showLeadingSpace ? /*#__PURE__*/_jsx(View, {
style: styles.scrollablePadding
}) : null, React.Children.map(children, (tab, tabIndex) => /*#__PURE__*/_jsx(TabsHeaderItem, {
theme: theme,
tabIndex: tabIndex,
tab: tab,
active: index === tabIndex,
onTabLayout: onTabLayout,
goTo: goTo,
activeColor: activeColor,
textColor: textColor,
position: position,
offset: offset,
childrenCount: children.length,
uppercase: uppercase,
iconPosition: iconPosition,
showTextLabel: showTextLabel,
mode: mode,
tabLabelStyle: tabLabelStyle
})), /*#__PURE__*/_jsx(Animated.View, {
ref: indicatorRef,
pointerEvents: "none",
style: [styles.indicator, {
backgroundColor: theme.colors.primary
}, indicatorStyle]
})]
}), elevation && /*#__PURE__*/_jsx(Animated.View, {
style: [styles.removeTopShadow, {
height: elevation,
backgroundColor,
top: -elevation
}]
})]
})
});
}
const styles = StyleSheet.create({
relative: {
position: 'relative'
},
removeTopShadow: {
position: 'absolute',
left: 0,
right: 0,
zIndex: 2
},
scrollablePadding: {
width: 52
},
tabs: {
height: 48
},
tabsTopIcon: {
height: 72
},
fixedContentContainerStyle: {
flex: 1
},
indicator: {
position: 'absolute',
height: 2,
width: 1,
left: 0,
bottom: 0,
...Platform.select({
web: {
backgroundColor: 'transparent',
transitionDuration: '150ms',
transitionProperty: 'all',
transformOrigin: 'left',
willChange: 'transform'
},
default: {}
})
}
});
//# sourceMappingURL=TabsHeader.js.map