react-native-collapsible-tab-view
Version:
Collapsible tab view component for React Native
186 lines (183 loc) • 7.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TABBAR_HEIGHT = exports.MaterialTabBar = void 0;
var _react = _interopRequireDefault(require("react"));
var _reactNative = require("react-native");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _Indicator = require("./Indicator");
var _TabItem = require("./TabItem");
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const TABBAR_HEIGHT = exports.TABBAR_HEIGHT = 48;
/**
* Basic usage looks like this:
*
* ```tsx
* <Tabs.Container
* ...
* TabBarComponent={(props) => (
* <MaterialTabBar
* {...props}
* activeColor="red"
* inactiveColor="yellow"
* inactiveOpacity={1}
* labelStyle={{ fontSize: 14 }}
* />
* )}
* >
* {...}
* </Tabs.Container>
* ```
*/
const MaterialTabBar = ({
tabNames,
indexDecimal,
scrollEnabled = false,
indicatorStyle,
index,
TabItemComponent = _TabItem.MaterialTabItem,
getLabelText = name => String(name).toUpperCase(),
onTabPress,
style,
tabProps,
contentContainerStyle,
labelStyle,
inactiveColor,
activeColor,
tabStyle,
width: customWidth,
keepActiveTabCentered
}) => {
const tabBarRef = (0, _reactNativeReanimated.useAnimatedRef)();
const windowWidth = (0, _reactNative.useWindowDimensions)().width;
const width = customWidth ?? windowWidth;
const isFirstRender = _react.default.useRef(true);
const itemLayoutGathering = _react.default.useRef(new Map());
const tabsOffset = (0, _reactNativeReanimated.useSharedValue)(0);
const isScrolling = (0, _reactNativeReanimated.useSharedValue)(false);
const nTabs = tabNames.length;
const [itemsLayout, setItemsLayout] = _react.default.useState(scrollEnabled ? [] : tabNames.map((_, i) => {
const tabWidth = width / nTabs;
return {
width: tabWidth,
x: i * tabWidth
};
}));
_react.default.useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
} else if (!scrollEnabled) {
// update items width on window resizing
const tabWidth = width / nTabs;
setItemsLayout(tabNames.map((_, i) => {
return {
width: tabWidth,
x: i * tabWidth
};
}));
}
}, [scrollEnabled, nTabs, tabNames, width]);
const onTabItemLayout = _react.default.useCallback((event, name) => {
if (scrollEnabled) {
if (!event.nativeEvent?.layout) return;
const {
width,
x
} = event.nativeEvent.layout;
itemLayoutGathering.current.set(name, {
width,
x
});
// pick out the layouts for the tabs we know about (in case they changed dynamically)
const layout = Array.from(itemLayoutGathering.current.entries()).filter(([tabName]) => tabNames.includes(tabName)).map(([, layout]) => layout).sort((a, b) => a.x - b.x);
if (layout.length === tabNames.length) {
setItemsLayout(layout);
}
}
}, [scrollEnabled, tabNames]);
const cancelNextScrollSync = (0, _reactNativeReanimated.useSharedValue)(index.value);
const onScroll = (0, _reactNativeReanimated.useAnimatedScrollHandler)({
onScroll: event => {
tabsOffset.value = event.contentOffset.x;
},
onBeginDrag: () => {
isScrolling.value = true;
cancelNextScrollSync.value = index.value;
},
onMomentumEnd: () => {
isScrolling.value = false;
}
}, []);
const currentIndexToSync = (0, _reactNativeReanimated.useSharedValue)(index.value);
const targetIndexToSync = (0, _reactNativeReanimated.useSharedValue)(index.value);
(0, _reactNativeReanimated.useAnimatedReaction)(() => {
return index.value;
}, nextIndex => {
if (scrollEnabled) {
(0, _reactNativeReanimated.cancelAnimation)(currentIndexToSync);
targetIndexToSync.value = nextIndex;
currentIndexToSync.value = (0, _reactNativeReanimated.withTiming)(nextIndex);
}
}, [scrollEnabled]);
(0, _reactNativeReanimated.useAnimatedReaction)(() => {
return currentIndexToSync.value === targetIndexToSync.value;
}, canSync => {
if (canSync && scrollEnabled && itemsLayout.length === nTabs && itemsLayout[index.value]) {
const halfTab = itemsLayout[index.value].width / 2;
const offset = itemsLayout[index.value].x;
if (keepActiveTabCentered || offset < tabsOffset.value || offset > tabsOffset.value + width - 2 * halfTab) {
(0, _reactNativeReanimated.scrollTo)(tabBarRef, offset - width / 2 + halfTab, 0, true);
}
}
}, [scrollEnabled, itemsLayout, nTabs]);
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.ScrollView, {
ref: tabBarRef,
horizontal: true,
style: style,
contentContainerStyle: [styles.contentContainer, !scrollEnabled && {
width
}, contentContainerStyle],
keyboardShouldPersistTaps: "handled",
bounces: false,
alwaysBounceHorizontal: false,
scrollsToTop: false,
showsHorizontalScrollIndicator: false,
automaticallyAdjustContentInsets: false,
overScrollMode: "never",
scrollEnabled: scrollEnabled,
onScroll: scrollEnabled ? onScroll : undefined,
scrollEventThrottle: 16,
children: [tabNames.map((name, i) => {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(TabItemComponent, {
index: i,
name: name,
label: tabProps.get(name)?.label || getLabelText(name),
onPress: onTabPress,
onLayout: scrollEnabled ? event => onTabItemLayout(event, name) : undefined,
scrollEnabled: scrollEnabled,
indexDecimal: indexDecimal,
labelStyle: labelStyle,
activeColor: activeColor,
inactiveColor: inactiveColor,
style: tabStyle
}, name);
}), itemsLayout.length === nTabs && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Indicator.Indicator, {
indexDecimal: indexDecimal,
itemsLayout: itemsLayout,
fadeIn: scrollEnabled,
style: indicatorStyle
})]
});
};
const MemoizedTabBar = exports.MaterialTabBar = /*#__PURE__*/_react.default.memo(MaterialTabBar);
const styles = _reactNative.StyleSheet.create({
contentContainer: {
flexDirection: 'row',
flexWrap: 'nowrap'
}
});
//# sourceMappingURL=TabBar.js.map