From c9fffbb9b4bc012c9c06ecdbbc1290291386d5ac Mon Sep 17 00:00:00 2001 From: Inbal Tish Date: Wed, 15 Feb 2023 11:22:58 +0200 Subject: [PATCH] TabBar - remove component --- src/components/index.js | 3 - src/components/tabBar/TabBarItem.tsx | 289 ---------------- src/components/tabBar/__tests__/index.spec.js | 29 -- src/components/tabBar/assets/gradient.png | Bin 1270 -> 0 bytes src/components/tabBar/index.tsx | 321 ------------------ src/index.ts | 1 - 6 files changed, 643 deletions(-) delete mode 100644 src/components/tabBar/TabBarItem.tsx delete mode 100644 src/components/tabBar/__tests__/index.spec.js delete mode 100644 src/components/tabBar/assets/gradient.png delete mode 100644 src/components/tabBar/index.tsx diff --git a/src/components/index.js b/src/components/index.js index bd7df46e9e..0af9cb35a5 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -143,9 +143,6 @@ export default { get TabController() { return require('./tabController').default; }, - get TabBar() { //TODO: remove on V7 - return require('./tabBar').default; - }, get ChipsInput() { return require('./ChipsInput').default; }, diff --git a/src/components/tabBar/TabBarItem.tsx b/src/components/tabBar/TabBarItem.tsx deleted file mode 100644 index 3cfd2a9cb3..0000000000 --- a/src/components/tabBar/TabBarItem.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import _ from 'lodash'; -import React, {PureComponent, ReactElement} from 'react'; -import { - StyleSheet, - Animated, - Easing, - LayoutChangeEvent, - LayoutRectangle, - StyleProp, - ViewStyle, - TextStyle -} from 'react-native'; -import {Colors, Typography, Spacings} from '../../style'; -import {Constants, asBaseComponent} from '../../commons/new'; -import View from '../view'; -import TouchableOpacity from '../touchableOpacity'; -import Text from '../text'; -import Image from '../image'; -import Badge, {BadgeProps} from '../badge'; - -const INDICATOR_HEIGHT = 2; -const INDICATOR_BG_COLOR = Colors.$backgroundPrimaryHeavy; -const HORIZONTAL_PADDING = Constants.isTablet ? Spacings.s7 : Spacings.s5; - -export interface TabBarItemProps { - /** - * icon of the tab - */ - icon?: number; - /** - * icon tint color - */ - iconColor?: string; - /** - * icon selected tint color - */ - iconSelectedColor?: string; - /** - * label of the tab - */ - label?: string; - /** - * custom label style - */ - labelStyle?: StyleProp; - /** - * Badge component's props to display next the item label - */ - badgeProps?: BadgeProps; - /** - * Pass to render a leading element - */ - leadingAccessory?: ReactElement; - /** - * Pass to render a trailing element - */ - trailingAccessory?: ReactElement; - /** - * maximum number of lines the label can break - */ - maxLines?: number; - /** - * custom selected tab label style - */ - selectedLabelStyle?: StyleProp; - /** - * whether the tab is selected or not - */ - selected?: boolean; - /** - * whether the tab will have a divider on its right - */ - showDivider?: boolean; - /** - * A fixed width for the item - */ - width?: number; - /** - * tabBar's background color - */ - backgroundColor?: string; - /** - * ignore of the tab - */ - ignore?: boolean; - /** - * callback for when pressing a tab - */ - onPress?: () => void; - /** - * whether to change the text to uppercase - */ - uppercase?: boolean; - /** - * Apply background color on press for TouchableOpacity - */ - activeBackgroundColor?: string; - accessibilityLabel?: string; - indicatorStyle?: StyleProp; // for inner use - style?: ViewStyle; - testID?: string; - children?: React.ReactNode; -} - -interface State { - indicatorOpacity: Animated.Value; - selected?: boolean; -} - -/** - * @description: TabBar.Item, inner component of TabBar for configuring the tabs - * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabBarScreen.tsx - * @extends: TouchableOpacity - * @extendsLink: https://reactnative.dev/docs/touchableopacity - */ -class TabBarItem extends PureComponent { - static displayName = 'TabBar.Item'; - - static defaultProps: Partial = { - maxLines: 1 - }; - - layout?: LayoutRectangle; - - constructor(props: TabBarItemProps) { - super(props); - - this.state = { - indicatorOpacity: props.selected ? new Animated.Value(1) : new Animated.Value(0), - selected: props.selected - }; - } - - componentDidUpdate(prevProps: TabBarItemProps) { - if (prevProps.selected !== this.props.selected) { - this.animate(this.props.selected); - } - } - - animate(newValue?: boolean) { - Animated.timing(this.state.indicatorOpacity, { - toValue: newValue ? 1 : 0, - easing: Easing.ease, - duration: 150, - useNativeDriver: true - }).start(this.onAnimateCompleted); - } - - onAnimateCompleted = () => { - this.setState({selected: this.props.selected}); - }; - - getFlattenStyle(style: StyleProp) { - return StyleSheet.flatten(style); - } - - getStylePropValue(flattenStyle: StyleProp, propName: string) { - let prop; - if (flattenStyle) { - const propObject: any = _.pick(flattenStyle, [propName]); - prop = propObject[propName]; - } - return prop; - } - - getColorFromStyle(style: StyleProp) { - const flattenStyle = this.getFlattenStyle(style); - return this.getStylePropValue(flattenStyle, 'color'); - } - - getLayout() { - return this.layout; - } - - onLayout = (event: LayoutChangeEvent) => { - this.layout = event.nativeEvent.layout; - }; - - render() { - const {indicatorOpacity, selected} = this.state; - const { - children, - indicatorStyle, - icon, - iconColor, - iconSelectedColor, - label, - labelStyle, - badgeProps, - leadingAccessory, - trailingAccessory, - uppercase, - maxLines, - selectedLabelStyle, - showDivider, - width, - onPress, - activeBackgroundColor, - backgroundColor, - testID, - accessibilityLabel, - style - } = this.props; - - const iconTint = iconColor || this.getColorFromStyle(labelStyle) || this.getColorFromStyle(styles.label); - const iconSelectedTint = - iconSelectedColor || this.getColorFromStyle(selectedLabelStyle) || this.getColorFromStyle(styles.selectedLabel); - const badgeSize = _.get(badgeProps, 'size', 16); - - return ( - - - {leadingAccessory} - {icon && ( - - )} - {!_.isEmpty(label) && ( - - {label} - - )} - {children} - {!_.isNil(badgeProps) && ( - - )} - {trailingAccessory} - - - - ); - } -} - -export default asBaseComponent(TabBarItem); - -const styles = StyleSheet.create({ - contentContainer: { - paddingHorizontal: HORIZONTAL_PADDING - }, - label: { - color: Colors.$textPrimary, - ...Typography.text80 - }, - selectedLabel: { - color: Colors.$textPrimary, - ...Typography.text80, - fontWeight: 'bold' - }, - divider: { - borderRightWidth: 1, - borderRightColor: Colors.grey70, - marginVertical: 14 // NOTE: will not cut long text at the top and bottom in iOS if TabBar not high enough - }, - indicator: { - backgroundColor: INDICATOR_BG_COLOR, - height: INDICATOR_HEIGHT, - marginHorizontal: HORIZONTAL_PADDING - }, - badge: { - marginLeft: Spacings.s1 - }, - icon: { - marginRight: 6 - } -}); diff --git a/src/components/tabBar/__tests__/index.spec.js b/src/components/tabBar/__tests__/index.spec.js deleted file mode 100644 index 7a1378ed43..0000000000 --- a/src/components/tabBar/__tests__/index.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -import TabBar from '../index'; - -describe.skip('TabBar', () => { - describe('calcIndicatorWidth', () => { - it('should equale 25%', () => { - const uut = new TabBar({}); - uut.itemsWidths = [80, 80, 80, 80]; - uut.state = { - selectedIndex: 1 - }; - uut.childrenCount = 4; // This fails because we change childrenCount into a getter - uut.contentWidth = 320; - jest.spyOn(uut, 'calcIndicatorWidth'); - expect(uut.calcIndicatorWidth()).toEqual('25%'); - }); - - it('should equale 50%', () => { - const uut = new TabBar({}); - uut.itemsWidths = [150, 150, 300]; - uut.state = { - selectedIndex: 2 - }; - uut.childrenCount = 2; - uut.contentWidth = 600; - jest.spyOn(uut, 'calcIndicatorWidth'); - expect(uut.calcIndicatorWidth()).toEqual('50%'); - }); - }); -}); diff --git a/src/components/tabBar/assets/gradient.png b/src/components/tabBar/assets/gradient.png deleted file mode 100644 index f0124935091e050b44f65f50edfdca3bc1cf4b04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1270 zcmV|(P!cmHM~DmnxwFQ-?Yb4y?K;(Fzo5u-Ol z9`uOPEYS6roWqUPyOKX?twpHw7Yy!y;QM2 zdc!z(sk$DzAM-rh<;|i~wf3w4)VHx`FK%^cE#NptwCJAXJ=>khurzQ2aHEwv&D9H=mWni zeALcQUNiO4SMj14?nhCsXYl0J)#!8aJlA8M-HUt1OL3+q|FF+f&)nzPj$3S=z1ov{ zJ^e;ibbuhks!zzfos(Zf7*YVasSlKRd4R? z@WeguEDP1jH>w5~!xif( z=Tf!Kba^jcueg~fyz_RybN0F4`CWJBMeE-vg&Mn{SkI0#hmOt%fBSV@zd5yXV5<3I zbEfW{%Hy4LJ>z}1AN?+0)NToe^3TNcbQ!uHIUI88Xyxji{%X$PvEcNWk0u9p&OSN3 z?=<``<%`-afjjYr)N{IYGnnJtOUltHwPsI#=Q-`aLw`NY#M^rno87tfuUE zmgNCc<~RdatX}WLoT<)%d%g`kd{MoWA_HAG15Pu{pBBoSWqz~d^z=Sr`nwtT)DJH2 z9{3YRyUQ2VOBL&*H;i+as_UWqG0(GI-Yhy*{$ZXcbFJ-I}xK_RJr~RiH_fOqi_2%AQUKHiU>~gm_^o9`Z6212N zpAV*@!em2{Xfb}^oQ;x void; - /** - * callback for when tab selected - */ - onTabSelected?: (index: number) => void; - /** - * custom style for the selected indicator - */ - indicatorStyle?: StyleProp; - /** - * Tab Bar height - */ - height?: number; - /** - * Pass when container width is different than the screen width - */ - containerWidth?: number; - /** - * The background color - */ - backgroundColor?: string; - /** - * set darkTheme style - */ - darkTheme?: boolean; - children?: React.ReactNode; - style?: ViewStyle; - testID?: string -} - -interface State { - scrollEnabled: boolean; - currentIndex: number; -} - -/** - * @description: TabBar Component - * @modifiers: alignment, flex, padding, margin, background, typography, color (list of supported modifiers) - * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabBarScreen.tsx - * @extends: ScrollBar - * @notes: This is screen width component. - */ -class TabBar extends Component { - static displayName = 'TabBar'; - - static defaultProps: Partial = { - selectedIndex: 0 - }; - - static Item = TabBarItem; - - scrollContentWidth?: number; - contentOffset: { - x: number; - y: number; - } - scrollBar: RefObject; - itemsRefs: ElementRef[]; - - constructor(props: TabBarProps) { - super(props); - - this.state = { - scrollEnabled: false, - currentIndex: props.selectedIndex || 0 - }; - - this.contentOffset = {x: 0, y: 0}; - this.scrollBar = React.createRef(); - this.itemsRefs = []; - - LogService.componentDeprecationWarn({oldComponent: 'TabBar', newComponent: 'TabController'}); - } - - componentDidUpdate(prevProps: TabBarProps, prevState: State) { - const prevChildrenCount = React.Children.count(prevProps.children); - if (this.childrenCount !== prevChildrenCount) { - this.updateIndicator(0); - } - // TODO: since we're implementing an uncontrolled component here, we should verify the selectedIndex has changed - // between this.props and nextProps (basically the meaning of selectedIndex should be initialIndex) - const isIndexManuallyChanged = - this.props.selectedIndex !== prevState.currentIndex && prevProps.selectedIndex !== this.props.selectedIndex; - if (isIndexManuallyChanged) { - this.updateIndicator(this.props.selectedIndex); - } - } - - // generateStyles() { - // this.styles = createStyles(this.props); - // } - - get childrenCount() { - return React.Children.count(this.props.children); - } - - get scrollContainerWidth() { - return this.props.containerWidth || Constants.screenWidth; - } - - isIgnored(index: number) { - const child = React.Children.toArray(this.props.children)[index]; - return _.get(child, 'props.ignore'); - } - - hasOverflow() { - return this.scrollContentWidth && this.scrollContentWidth > this.scrollContainerWidth; - } - - shouldBeMarked = (index: number) => { - return this.state.currentIndex === index && !this.isIgnored(index) && this.childrenCount > 1; - }; - - updateIndicator(index?: number) { - if (index !== undefined && !this.isIgnored(index)) { - this.setState({currentIndex: index}, () => { - this.scrollToSelected(); - }); - } - } - - scrollToSelected(animated = true) { - const childRef: any = this.itemsRefs[this.state.currentIndex]; - const childLayout = childRef.getLayout(); - - if (childLayout && this.hasOverflow()) { - if (childLayout.x + childLayout.width - this.contentOffset.x > this.scrollContainerWidth) { - this.scrollBar?.current?.scrollTo?.({x: childLayout.x - this.scrollContainerWidth + childLayout.width, y: 0, animated}); - } else if (childLayout.x - this.contentOffset.x < 0) { - this.scrollBar?.current?.scrollTo?.({x: childLayout.x, y: 0, animated}); - } - } - } - - onChangeIndex(index: number) { - this.props.onChangeIndex?.(index); - } - - onTabSelected(index: number) { - this.props.onTabSelected?.(index); - - } - - onItemPress = (index: number, props: TabBarItemProps) => { - this.updateIndicator(index); - - setTimeout(() => { - if (!props.ignore) { - this.onChangeIndex(index); - } - this.onTabSelected(index); - props.onPress?.(); - }, 0); - }; - - onScroll = (event: NativeSyntheticEvent) => { - const {contentOffset} = event.nativeEvent; - this.contentOffset = contentOffset; - }; - - onContentSizeChange = (width: number) => { - if (this.scrollContentWidth !== width) { - this.scrollContentWidth = width; - const {minTabsForScroll} = this.props; - const minChildrenCount = minTabsForScroll || MIN_TABS_FOR_SCROLL; - if (this.hasOverflow() && this.childrenCount > minChildrenCount) { - this.setState({scrollEnabled: true}); - } - } - }; - - renderTabBar() { - const {height, backgroundColor = DEFAULT_BACKGROUND_COLOR, containerView, containerProps, gradientMargins} = this.props; - const {scrollEnabled} = this.state; - const containerHeight = height || DEFAULT_HEIGHT; - - return ( - - - {this.renderChildren()} - - - ); - } - - renderChildren() { - this.itemsRefs = []; - const {indicatorStyle, darkTheme} = this.props; - - const children = React.Children.map(this.props.children, (child, index) => { - // @ts-ignore - const accessLabel = child?.props.accessibilityLabel || child.props.label || ''; - - //TODO: review it again, all types here should be correct. As from React.Children.map it gets definitely child: React.ReactNode, and React.cloneElement does not accept it. - // But seems it's work in a real life, so maybe it is just trouble with types compatibility - //@ts-ignore - return React.cloneElement(child, { - indicatorStyle, - darkTheme, - selected: this.shouldBeMarked(index), - onPress: () => { - // @ts-ignore - this.onItemPress(index, child.props); - }, - ref: (r: any) => { - this.itemsRefs[index] = r; - }, - accessibilityLabel: `${accessLabel} ${index + 1} out of ${this.childrenCount}` - }); - }); - return children; - } - - render() { - const {enableShadow, style, backgroundColor = DEFAULT_BACKGROUND_COLOR} = this.props; - - return ( - // @ts-ignore - - {this.renderTabBar()} - - ); - } -} - -export default asBaseComponent(TabBar); - - -const styles = StyleSheet.create({ - container: { - zIndex: 100 - }, - containerShadow: { - ...Platform.select({ - ios: { - shadowColor: Colors.grey10, - shadowOpacity: 0.05, - shadowRadius: 2, - shadowOffset: {height: 6, width: 0} - }, - android: { - elevation: 5, - backgroundColor: Colors.white - } - }) - }, - tabBar: { - flex: 1 - }, - shadowImage: { - width: '100%' - }, - scrollBarContainer: { - minWidth: '100%' - } -}); diff --git a/src/index.ts b/src/index.ts index 634fd2982d..cae780c246 100644 --- a/src/index.ts +++ b/src/index.ts @@ -146,7 +146,6 @@ export { TabControllerItemProps, TabControllerImperativeMethods } from './components/tabController'; -export {default as TabBar, TabBarProps} from './components/tabBar'; //TODO: remove on V7 export { default as Timeline, TimelineProps,