diff --git a/demo/src/screens/MenuStructure.js b/demo/src/screens/MenuStructure.js index 4b85987a68..3b2bfbe003 100644 --- a/demo/src/screens/MenuStructure.js +++ b/demo/src/screens/MenuStructure.js @@ -114,6 +114,7 @@ export const navigationData = { {title: 'Modal', tags: 'modal topbar screen', screen: 'unicorn.screens.ModalScreen'}, {title: 'StateScreen', tags: 'empty state screen', screen: 'unicorn.screens.EmptyStateScreen'}, {title: 'TabController', tags: 'tabbar controller native', screen: 'unicorn.components.TabControllerScreen'}, + {title: 'TabControllerWithStickyHeader', tags: 'tabbar controller native sticky header', screen: 'unicorn.components.TabControllerWithStickyHeaderScreen'}, {title: 'Timeline', tags: 'timeline', screen: 'unicorn.components.TimelineScreen'}, { title: 'withScrollEnabler', diff --git a/demo/src/screens/__tests__/__snapshots__/TextFieldScreen.spec.js.snap b/demo/src/screens/__tests__/__snapshots__/TextFieldScreen.spec.js.snap index b5e809facf..cd6d85d9b8 100644 --- a/demo/src/screens/__tests__/__snapshots__/TextFieldScreen.spec.js.snap +++ b/demo/src/screens/__tests__/__snapshots__/TextFieldScreen.spec.js.snap @@ -3521,8 +3521,8 @@ exports[`TextField Screen renders screen 1`] = ` "marginLeft": 20, "minWidth": 66, "opacity": 1, - "paddingHorizontal": 10, - "paddingVertical": 2, + "paddingHorizontal": 11, + "paddingVertical": 3, } } > diff --git a/demo/src/screens/componentScreens/GridViewScreen.tsx b/demo/src/screens/componentScreens/GridViewScreen.tsx index 152b6e8ecf..aea16a09b3 100644 --- a/demo/src/screens/componentScreens/GridViewScreen.tsx +++ b/demo/src/screens/componentScreens/GridViewScreen.tsx @@ -12,6 +12,7 @@ class GridViewScreen extends Component { _.map(contacts, contact => ({ imageProps: {source: {uri: contact.thumbnail}, borderRadius: 999, style: {backgroundColor: Colors.grey60}}, title: _.split(contact.name, ' ')[0], + titleLines: 1, onPress: () => Alert.alert('My name is ' + contact.name) })))(conversations), products: _.flow(products => _.take(products, 8), @@ -174,7 +175,7 @@ class GridViewScreen extends Component { Dynamic itemSize - (Using maxItemWidth) + (Using maxItemWidth) diff --git a/demo/src/screens/componentScreens/NumberInputScreen.tsx b/demo/src/screens/componentScreens/NumberInputScreen.tsx index c561970b84..f35c92d33d 100644 --- a/demo/src/screens/componentScreens/NumberInputScreen.tsx +++ b/demo/src/screens/componentScreens/NumberInputScreen.tsx @@ -111,6 +111,7 @@ const NumberInputScreen = () => { if (currentData.current?.type === 'valid') { return currentData.current.number > MINIMUM_PRICE; } + return false; }, []); const isWithinDiscountPercentage = useCallback(() => { @@ -119,6 +120,7 @@ const NumberInputScreen = () => { currentData.current.number >= DISCOUNT_PERCENTAGE.min && currentData.current.number <= DISCOUNT_PERCENTAGE.max ); } + return false; }, []); const validate = useMemo((): Incubator.TextFieldProps['validate'] => { diff --git a/demo/src/screens/componentScreens/TabControllerWithStickyHeaderScreen.tsx b/demo/src/screens/componentScreens/TabControllerWithStickyHeaderScreen.tsx new file mode 100644 index 0000000000..41fd1ed0cf --- /dev/null +++ b/demo/src/screens/componentScreens/TabControllerWithStickyHeaderScreen.tsx @@ -0,0 +1,68 @@ +import React, {Component} from 'react'; +import {ScrollView} from 'react-native'; +import {View, Text, Card, Image, TabController} from 'react-native-ui-lib'; +import _ from 'lodash'; + +const IMAGE_URL = + 'https://images.pexels.com/photos/1598505/pexels-photo-1598505.jpeg?auto=compress&cs=tinysrgb&w=500&dpr=1'; +const items = [ + {key: 'tab1', label: 'Tab 1'}, + {key: 'tab2', label: 'Tab 2'} +]; + +export default class TabControllerWithStickyHeaderScreen extends Component { + renderHeader = () => { + return ( + + + + ); + }; + + renderTab1 = () => { + return ( + + {_.times(7, i => { + return ( + + item {i} + + ); + })} + + ); + }; + + renderTab2 = () => { + return ( + + {_.times(15, i => { + return ( + + item {i} + + ); + })} + + ); + }; + + render() { + return ( + + + {this.renderHeader()} + + + + {this.renderTab1()} + {this.renderTab2()} + + + + ); + } +} diff --git a/demo/src/screens/componentScreens/index.js b/demo/src/screens/componentScreens/index.js index 3c764feeba..630ecdcb86 100644 --- a/demo/src/screens/componentScreens/index.js +++ b/demo/src/screens/componentScreens/index.js @@ -15,7 +15,7 @@ export function registerScreens(registrar) { registrar('unicorn.components.CheckboxScreen', () => require('./CheckboxScreen').default); registrar('unicorn.components.ChipScreen', () => require('./ChipScreen').default); registrar('unicorn.components.ChipsInputScreen', () => require('./ChipsInputScreen').default); - registrar('unicorn.components.ColorPickerScreen', () => require('./ColorPickerScreen').default); + registrar('unicorn.components.ColorPickerScreen', () => gestureHandlerRootHOC(require('./ColorPickerScreen').default)); registrar('unicorn.components.ColorSwatchScreen', () => require('./ColorSwatchScreen').default); registrar('unicorn.components.ConnectionStatusBar', () => require('./ConnectionStatusBarScreen').default); registrar('unicorn.components.DateTimePickerScreen', () => require('./DateTimePickerScreen').default); @@ -28,7 +28,7 @@ export function registerScreens(registrar) { registrar('unicorn.components.IconScreen', () => require('./IconScreen').default); registrar('unicorn.components.ImageScreen', () => require('./ImageScreen').default); registrar('unicorn.components.GridListScreen', () => require('./GridListScreen').default); - registrar('unicorn.components.GridViewScreen', () => require('./GridViewScreen').default); + registrar('unicorn.components.GridViewScreen', () => gestureHandlerRootHOC(require('./GridViewScreen').default)); registrar('unicorn.components.KeyboardAwareScrollViewScreen', () => require('./KeyboardAwareScrollViewScreen').default); registrar('unicorn.components.MaskedInputScreen', () => require('./MaskedInputScreen').default); registrar('unicorn.components.MarqueeScreen', () => require('./MarqueeScreen').default); @@ -38,12 +38,12 @@ export function registerScreens(registrar) { registrar('unicorn.components.PanDismissibleScreen', () => require('./PanDismissibleScreen').default); registrar('unicorn.components.PanListenerScreen', () => require('./PanListenerScreen').default); registrar('unicorn.components.PanResponderScreen', () => require('./PanResponderScreen').default); - registrar('unicorn.components.PickerScreen', () => require('./PickerScreen').default); + registrar('unicorn.components.PickerScreen', () => gestureHandlerRootHOC(require('./PickerScreen').default)); registrar('unicorn.animations.ProgressBarScreen', () => require('../componentScreens/ProgressBarScreen').default); registrar('unicorn.components.ProgressiveImageScreen', () => require('./ProgressiveImageScreen').default); registrar('unicorn.components.RadioButtonScreen', () => require('./RadioButtonScreen').default); registrar('unicorn.components.ScrollBarScreen', () => require('./ScrollBarScreen').default); - registrar('unicorn.components.SectionsWheelPickerScreen', () => require('./SectionsWheelPickerScreen').default); + registrar('unicorn.components.SectionsWheelPickerScreen', () => gestureHandlerRootHOC(require('./SectionsWheelPickerScreen').default)); registrar('unicorn.components.SegmentedControlScreen', () => require('./SegmentedControlScreen').default); registrar('unicorn.components.SharedTransitionScreen', () => require('./SharedTransitionScreen').default); registrar('unicorn.components.SkeletonViewScreen', () => require('./SkeletonViewScreen').default); @@ -55,6 +55,7 @@ export function registerScreens(registrar) { registrar('unicorn.components.StepperScreen', () => require('./StepperScreen').default); registrar('unicorn.components.SwitchScreen', () => require('./SwitchScreen').default); registrar('unicorn.components.TabControllerScreen', () => require('./TabControllerScreen').default); + registrar('unicorn.components.TabControllerWithStickyHeaderScreen', () => gestureHandlerRootHOC(require('./TabControllerWithStickyHeaderScreen').default)); registrar('unicorn.components.TextFieldScreen', () => require('./TextFieldScreen').default); registrar('unicorn.components.TextScreen', () => require('./TextScreen').default); registrar('unicorn.components.ToastsScreen', () => require('./ToastsScreen').default); diff --git a/demo/src/screens/incubatorScreens/PanViewScreen.tsx b/demo/src/screens/incubatorScreens/PanViewScreen.tsx index efc4c5bcf5..ce15754904 100644 --- a/demo/src/screens/incubatorScreens/PanViewScreen.tsx +++ b/demo/src/screens/incubatorScreens/PanViewScreen.tsx @@ -146,9 +146,8 @@ class PanViewScreen extends Component { render() { const {showToast, showDialog} = this.state; - const Container = showDialog ? View : GestureHandlerRootView; return ( - + New Pan View @@ -164,7 +163,7 @@ class PanViewScreen extends Component { {showToast && this.renderToast()} {showDialog && this.renderDialog()} - + ); } } diff --git a/package.json b/package.json index 86120ef9ce..76624636b8 100644 --- a/package.json +++ b/package.json @@ -106,12 +106,12 @@ "react-dom": "^18.2.0", "react-native": "0.71.12", "react-native-fs": "^2.20.0", - "react-native-gesture-handler": "2.9.0", + "react-native-gesture-handler": "2.14.1", "react-native-haptic-feedback": "^1.11.0", "react-native-linear-gradient": "2.6.2", "react-native-mmkv": "2.11.0", "react-native-navigation": "7.32.1", - "react-native-reanimated": "3.4.0", + "react-native-reanimated": "3.8.1", "react-native-shimmer-placeholder": "^2.0.6", "react-native-svg": "^13.7.0", "react-native-svg-transformer": "1.1.0", diff --git a/src/components/WheelPicker/index.tsx b/src/components/WheelPicker/index.tsx index 92d1f5b831..f133338249 100644 --- a/src/components/WheelPicker/index.tsx +++ b/src/components/WheelPicker/index.tsx @@ -349,6 +349,7 @@ const WheelPicker = (props: WheelPickerProps) return ( + {separators} (props: WheelPickerProps) {label && labelContainer} {fader(FaderPosition.BOTTOM)} {fader(FaderPosition.TOP)} - {separators} ); }; diff --git a/src/components/button/Button.driver.new.ts b/src/components/button/Button.driver.new.ts index 3953d0fbdb..1160c84c1a 100644 --- a/src/components/button/Button.driver.new.ts +++ b/src/components/button/Button.driver.new.ts @@ -1,3 +1,4 @@ +import {StyleSheet} from 'react-native'; import {useComponentDriver, ComponentProps, usePressableDriver, TextDriver, ImageDriver} from '../../testkit'; export const ButtonDriver = (props: ComponentProps) => { @@ -17,9 +18,17 @@ export const ButtonDriver = (props: ComponentProps) => { return labelDriver; }; + const getLabelStyle = () => { + return labelDriver?.getStyle(); + }; + const getIcon = () => { return iconDriver; }; - return {getLabel, getIcon, ...driver}; + const getIconStyle = () => { + return StyleSheet.flatten(iconDriver?.getElement().props.style); + }; + + return {getLabel, getLabelStyle, getIconStyle, getIcon, ...driver}; }; diff --git a/src/components/button/__tests__/__snapshots__/index.spec.js.snap b/src/components/button/__tests__/__snapshots__/index.spec.js.snap index 359617374d..b0b2837880 100644 --- a/src/components/button/__tests__/__snapshots__/index.spec.js.snap +++ b/src/components/button/__tests__/__snapshots__/index.spec.js.snap @@ -482,8 +482,8 @@ exports[`Button backgroundColor should return undefined if this button is outlin "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 19, - "paddingVertical": 8.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > @@ -1230,8 +1230,8 @@ exports[`Button container size should reduce padding by outlineWidth in case of "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 18, - "paddingVertical": 7.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > @@ -1408,8 +1408,8 @@ exports[`Button container size should return style for large button 2`] = ` "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 19, - "paddingVertical": 8.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > @@ -1591,8 +1591,8 @@ exports[`Button container size should return style for medium button 2`] = ` "justifyContent": "center", "minWidth": 77, "opacity": 1, - "paddingHorizontal": 15, - "paddingVertical": 5.5, + "paddingHorizontal": 16, + "paddingVertical": 6.5, } } > @@ -1867,8 +1867,8 @@ exports[`Button container size should return style for small button 2`] = ` "justifyContent": "center", "minWidth": 70, "opacity": 1, - "paddingHorizontal": 13, - "paddingVertical": 3.5, + "paddingHorizontal": 14, + "paddingVertical": 4.5, } } > @@ -2055,8 +2055,8 @@ exports[`Button container size should return style for xSmall button 2`] = ` "justifyContent": "center", "minWidth": 66, "opacity": 1, - "paddingHorizontal": 10, - "paddingVertical": 2, + "paddingHorizontal": 11, + "paddingVertical": 3, } } > @@ -4191,8 +4191,8 @@ exports[`Button outline should render button with an outline 1`] = ` "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 19, - "paddingVertical": 8.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > @@ -4371,8 +4371,8 @@ exports[`Button outline should render button with outline and outlineColor 1`] = "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 19, - "paddingVertical": 8.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > @@ -4461,8 +4461,8 @@ exports[`Button outline should return custom borderWidth according to outlineWid "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 17, - "paddingVertical": 6.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > @@ -4551,8 +4551,8 @@ exports[`Button outline should return disabled color for outline if button is di "justifyContent": "center", "minWidth": 90, "opacity": 1, - "paddingHorizontal": 19, - "paddingVertical": 8.5, + "paddingHorizontal": 20, + "paddingVertical": 9.5, } } > diff --git a/src/components/button/index.tsx b/src/components/button/index.tsx index 0f4f774128..486ef756d2 100644 --- a/src/components/button/index.tsx +++ b/src/components/button/index.tsx @@ -149,16 +149,8 @@ class Button extends PureComponent { } getContainerSizeStyle() { - const { - outline, - avoidMinWidth, - avoidInnerPadding, - round, - size: propsSize, - outlineWidth: propsOutlineWidth - } = this.props; + const {avoidMinWidth, avoidInnerPadding, round, size: propsSize} = this.props; const size = propsSize || DEFAULT_SIZE; - const outlineWidth = propsOutlineWidth || 1; const CONTAINER_STYLE_BY_SIZE: Dictionary = {}; CONTAINER_STYLE_BY_SIZE[Button.sizes.xSmall] = round @@ -190,17 +182,6 @@ class Button extends PureComponent { minWidth: MIN_WIDTH.LARGE }; - if (outline) { - _.forEach(CONTAINER_STYLE_BY_SIZE, style => { - if (round) { - style.padding -= outlineWidth; // eslint-disable-line - } else { - style.paddingVertical -= outlineWidth; // eslint-disable-line - style.paddingHorizontal -= outlineWidth; // eslint-disable-line - } - }); - } - const containerSizeStyle = CONTAINER_STYLE_BY_SIZE[size]; if (this.isLink || (this.isIconButton && !round)) { diff --git a/src/components/chip/index.tsx b/src/components/chip/index.tsx index 7da3edd408..6e2f94cc38 100644 --- a/src/components/chip/index.tsx +++ b/src/components/chip/index.tsx @@ -17,7 +17,7 @@ export type ChipProps = ViewProps & /** * Chip's size. Number or a width and height object. */ - size?: number | {width: number; height: number}; + size?: number | Partial<{width: number; height: number}>; /** * On Chip press callback */ diff --git a/src/components/gridListItem/index.tsx b/src/components/gridListItem/index.tsx index 9f39b52425..8b5968a870 100644 --- a/src/components/gridListItem/index.tsx +++ b/src/components/gridListItem/index.tsx @@ -213,7 +213,7 @@ class GridListItem extends Component { accessible={renderCustomItem ? true : undefined} {...Modifiers.extractAccessibilityProps(this.props)} > - {imageProps && } + {imageProps && } {!_.isNil(renderCustomItem) && {renderCustomItem()}} {renderOverlay && {renderOverlay()}} diff --git a/src/components/marquee/index.tsx b/src/components/marquee/index.tsx index 0e83b9cb3c..9c9a7d8620 100644 --- a/src/components/marquee/index.tsx +++ b/src/components/marquee/index.tsx @@ -76,7 +76,6 @@ function Marquee(props: MarqueeProps) { } }, [viewLayout, textLayout]); - // @ts-expect-error should be fixed in version 3.5 (https://github.com/software-mansion/react-native-reanimated/pull/4881) const translateStyle = useAnimatedStyle(() => { if (offset.value) { return { @@ -90,7 +89,6 @@ function Marquee(props: MarqueeProps) { return ( - {/* @ts-expect-error should be fixed in version 3.5 (https://github.com/software-mansion/react-native-reanimated/pull/4881) */} {label} diff --git a/src/components/modal/api/modal.api.json b/src/components/modal/api/modal.api.json index 85c56251b7..7785707398 100644 --- a/src/components/modal/api/modal.api.json +++ b/src/components/modal/api/modal.api.json @@ -33,8 +33,7 @@ { "name": "useGestureHandlerRootView", "type": "boolean", - "description": "Should add a GestureHandlerRootView", - "note": "Android only" + "description": "Should add a GestureHandlerRootView" } ], "snippet": [ diff --git a/src/components/modal/index.tsx b/src/components/modal/index.tsx index 2329d219dd..482bc998dd 100644 --- a/src/components/modal/index.tsx +++ b/src/components/modal/index.tsx @@ -45,7 +45,7 @@ export interface ModalProps extends RNModalProps { */ accessibilityLabel?: string; /** - * Should add a GestureHandlerRootView (Android only) + * Should add a GestureHandlerRootView */ useGestureHandlerRootView?: boolean; /** @@ -112,9 +112,8 @@ class Modal extends Component { ...others } = this.props; const defaultContainer = enableModalBlur && Constants.isIOS && BlurView ? BlurView : View; - const useGestureHandler = useGestureHandlerRootView && Constants.isAndroid; - const GestureContainer = useGestureHandler ? GestureHandlerRootView : React.Fragment; - const gestureContainerProps = useGestureHandler ? {style: styles.fill} : {}; + const GestureContainer = useGestureHandlerRootView ? GestureHandlerRootView : React.Fragment; + const gestureContainerProps = useGestureHandlerRootView ? {style: styles.fill} : {}; const useKeyboardAvoiding = useKeyboardAvoidingView && Constants.isIOS; const KeyboardAvoidingContainer = useKeyboardAvoiding ? KeyboardAvoidingView : React.Fragment; const keyboardAvoidingContainerProps = useKeyboardAvoiding diff --git a/src/components/picker/PickerItemsList.tsx b/src/components/picker/PickerItemsList.tsx index edbeafbc76..26db3b6e97 100644 --- a/src/components/picker/PickerItemsList.tsx +++ b/src/components/picker/PickerItemsList.tsx @@ -168,7 +168,7 @@ const PickerItemsList = (props: PickerItemsListProps) => { }; return ( - + {renderPickerHeader()} {renderContent()} diff --git a/src/components/sortableGridList/SortableItem.tsx b/src/components/sortableGridList/SortableItem.tsx index 6f7c4ee023..cad24f8ae2 100644 --- a/src/components/sortableGridList/SortableItem.tsx +++ b/src/components/sortableGridList/SortableItem.tsx @@ -171,7 +171,6 @@ function SortableItem(props: PropsWithChildren { const scale = withSpring(isDragging.value ? 1.1 : 1); const zIndex = isDragging.value ? 100 : withTiming(0, animationConfig); @@ -183,7 +182,6 @@ function SortableItem(props: PropsWithChildren {props.children} diff --git a/src/components/tabController/PageCarousel.tsx b/src/components/tabController/PageCarousel.tsx index d9974415d4..484f70c57e 100644 --- a/src/components/tabController/PageCarousel.tsx +++ b/src/components/tabController/PageCarousel.tsx @@ -104,7 +104,6 @@ function PageCarousel(props: ScrollViewProps) { ; onLayout?: (event: LayoutChangeEvent, index: number) => void; } diff --git a/src/components/tabController/TabPage.tsx b/src/components/tabController/TabPage.tsx index eddf6f4667..20a22bfbb5 100644 --- a/src/components/tabController/TabPage.tsx +++ b/src/components/tabController/TabPage.tsx @@ -1,5 +1,5 @@ import React, {PropsWithChildren, useCallback, useContext, useState, useMemo} from 'react'; -import {StyleSheet} from 'react-native'; +import {type StyleProp, StyleSheet, type ViewStyle} from 'react-native'; import Reanimated, {useAnimatedStyle, useAnimatedReaction, runOnJS} from 'react-native-reanimated'; // import {Freeze} from 'react-freeze'; import TabBarContext from './TabBarContext'; @@ -25,6 +25,10 @@ export interface TabControllerPageProps { * Used as a testing identifier */ testID?: string; + /** + * add style properties to tab page + */ + style?: StyleProp; } /** @@ -36,10 +40,11 @@ export default function TabPage({ index, lazy, renderLoading, + style, lazyLoadTime = 100, ...props }: PropsWithChildren) { - const {currentPage, asCarousel, containerWidth} = useContext(TabBarContext); + const {currentPage, asCarousel, nestedInScrollView, containerWidth} = useContext(TabBarContext); const [shouldLoad, setLoaded] = useState(!lazy); // const [focused, setFocused] = useState(false); @@ -75,18 +80,26 @@ export default function TabPage({ const animatedPageStyle = useAnimatedStyle(() => { const isActive = Math.round(currentPage.value) === index; - return { + + // TODO: Fix to proper animated style once Reanimated export AnimatedStyleProp + const style: any = { opacity: isActive || asCarousel ? 1 : 0, zIndex: isActive || asCarousel ? 1 : 0 }; + + if (nestedInScrollView) { + style.position = isActive ? 'relative' : 'absolute'; + } + + return style; }); - const style = useMemo(() => { - return [!asCarousel && styles.page, animatedPageStyle, {width: asCarousel ? containerWidth : undefined}]; - }, [asCarousel, animatedPageStyle, containerWidth]); + const _style = useMemo(() => { + return [!asCarousel && styles.page, animatedPageStyle, {width: asCarousel ? containerWidth : undefined}, style]; + }, [asCarousel, animatedPageStyle, containerWidth, style]); return ( - + {!shouldLoad && renderLoading?.()} {shouldLoad && props.children} {/* {props.children} */} diff --git a/src/components/tabController/apis/tabController.api.json b/src/components/tabController/apis/tabController.api.json index 5bea7bd9b5..f838bfbfae 100644 --- a/src/components/tabController/apis/tabController.api.json +++ b/src/components/tabController/apis/tabController.api.json @@ -19,6 +19,13 @@ "description": "When using TabController.PageCarousel this should be turned on", "default": "false" }, + { + "name": "nestedInScrollView", + "type": "boolean", + "description": "Pass when TabController is render inside a ScrollView (with a header)", + "note": "Does not work with asCarousel", + "default": "false" + }, {"name": "carouselPageWidth;", "type": "number", "description": "Pass for custom carousel page width"} ], "snippet": [ diff --git a/src/components/tabController/index.tsx b/src/components/tabController/index.tsx index 6a39a9015a..88f20ca6d6 100644 --- a/src/components/tabController/index.tsx +++ b/src/components/tabController/index.tsx @@ -37,6 +37,10 @@ export interface TabControllerProps { * When using TabController.PageCarousel this should be turned on */ asCarousel?: boolean; + /** + * Pass when TabController is render inside a ScrollView (with a header) + */ + nestedInScrollView?: boolean; /** * Pass for custom carousel page width */ @@ -65,6 +69,7 @@ const TabController = React.forwardRef((props: PropsWithChildren{children}; }); diff --git a/src/components/tabController/useScrollToItem.ts b/src/components/tabController/useScrollToItem.ts index 6aeac8550d..ffbd7ab99c 100644 --- a/src/components/tabController/useScrollToItem.ts +++ b/src/components/tabController/useScrollToItem.ts @@ -100,8 +100,8 @@ const useScrollToItem = (props: ScrollToItemPr innerSpacing = 0 } = props; const itemsWidths = useRef<(number | null)[]>(_.times(itemsCount, () => null)); - const itemsWidthsAnimated = useSharedValue(_.times(itemsCount, () => 0)); - const itemsOffsetsAnimated = useSharedValue(_.times(itemsCount, () => 0)); + const itemsWidthsAnimated = useSharedValue(_.times(itemsCount, () => 0)); + const itemsOffsetsAnimated = useSharedValue(_.times(itemsCount, () => 0)); const currentIndex = useRef(selectedIndex || 0); const [offsets, setOffsets] = useState({CENTER: [], LEFT: [], RIGHT: []}); const {scrollViewRef, scrollTo, onContentSizeChange, onLayout} = useScrollTo({scrollViewRef: propsScrollViewRef}); @@ -128,13 +128,6 @@ const useScrollToItem = (props: ScrollToItemPr const rightOffsets = []; rightOffsets.push(-containerWidth + widths[0] + outerSpacing + innerSpacing); while (index < itemsCount) { - /* map animated widths and offsets */ - itemsWidthsAnimated.value[index] = widths[index]; - if (index > 0) { - itemsOffsetsAnimated.value[index] = - itemsOffsetsAnimated.value[index - 1] + itemsWidthsAnimated.value[index - 1]; - } - /* calc center, left and right offsets */ centeredOffsets[index] = currentCenterOffset - screenCenter + widths[index] / 2; ++index; @@ -154,9 +147,22 @@ const useScrollToItem = (props: ScrollToItemPr setOffsets({CENTER: centeredOffsets, LEFT: leftOffsets, RIGHT: rightOffsets}); // default for DYNAMIC is CENTER - // trigger value change - itemsWidthsAnimated.value = [...itemsWidthsAnimated.value]; - itemsOffsetsAnimated.value = [...itemsOffsetsAnimated.value]; + // Update shared values + // @ts-expect-error pretty sure this is a bug in reanimated since itemsWidthsAnimated is defined as SharedValue + itemsWidthsAnimated.modify((value) => { + 'worklet'; + return value.map((_, index) => widths[index]); + }); + + itemsOffsetsAnimated.modify((value) => { + 'worklet'; + value.forEach((_, index) => { + if (index > 0) { + value[index] = value[index - 1] + widths[index - 1]; + } + }); + return value; + }); }, [itemsCount, outerSpacing, innerSpacing, addOffsetMargin, containerWidth]); diff --git a/src/components/textField/ClearButton.tsx b/src/components/textField/ClearButton.tsx index 749c6c01da..cff3c3fda9 100644 --- a/src/components/textField/ClearButton.tsx +++ b/src/components/textField/ClearButton.tsx @@ -22,7 +22,6 @@ const ClearButton = ({testID, onClear, onChangeText}: Pick { return { transform: [{translateY: animatedValue.value}, {translateX: 0}], @@ -49,7 +48,6 @@ const ClearButton = ({testID, onClear, onChangeText}: Pick