@hero-design/rn 8.12.3 → 8.12.5-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/.turbo/turbo-build:types.log +2 -0
  3. package/.turbo/turbo-lint.log +149 -0
  4. package/.turbo/turbo-publish:npm.log +9 -0
  5. package/es/index.js +38 -11
  6. package/lib/index.js +38 -11
  7. package/package.json +5 -5
  8. package/src/components/Tabs/ScrollableTabs.tsx +208 -167
  9. package/src/components/Tabs/__tests__/ScrollableTabs.spec.tsx +58 -11
  10. package/src/types.ts +2 -0
  11. package/types/components/Accordion/AccordionItem.d.ts +0 -0
  12. package/types/components/Accordion/StyledAccordion.d.ts +0 -0
  13. package/types/components/Accordion/index.d.ts +0 -0
  14. package/types/components/Alert/StyledAlert.d.ts +0 -0
  15. package/types/components/Alert/index.d.ts +0 -0
  16. package/types/components/Attachment/StyledAttachment.d.ts +0 -0
  17. package/types/components/Attachment/index.d.ts +0 -0
  18. package/types/components/Avatar/Avatar.d.ts +0 -0
  19. package/types/components/Avatar/AvatarStack/StyledAvatarStack.d.ts +0 -0
  20. package/types/components/Avatar/AvatarStack/index.d.ts +0 -0
  21. package/types/components/Avatar/AvatarStack/utils.d.ts +0 -0
  22. package/types/components/Avatar/StyledAvatar.d.ts +0 -0
  23. package/types/components/Avatar/index.d.ts +0 -0
  24. package/types/components/Badge/Status.d.ts +0 -0
  25. package/types/components/Badge/StyledBadge.d.ts +0 -0
  26. package/types/components/Badge/index.d.ts +0 -0
  27. package/types/components/BottomNavigation/StyledBottomNavigation.d.ts +0 -0
  28. package/types/components/BottomNavigation/index.d.ts +0 -0
  29. package/types/components/BottomSheet/BottomSheetContext.d.ts +0 -0
  30. package/types/components/BottomSheet/Footer.d.ts +0 -0
  31. package/types/components/BottomSheet/Header.d.ts +0 -0
  32. package/types/components/BottomSheet/ScrollView.d.ts +0 -0
  33. package/types/components/BottomSheet/StyledBottomSheet.d.ts +0 -0
  34. package/types/components/BottomSheet/index.d.ts +0 -0
  35. package/types/components/Box/StyledBox.d.ts +0 -0
  36. package/types/components/Box/config.d.ts +0 -0
  37. package/types/components/Box/index.d.ts +0 -0
  38. package/types/components/Box/types.d.ts +0 -0
  39. package/types/components/Button/Button.d.ts +0 -0
  40. package/types/components/Button/IconButton.d.ts +0 -0
  41. package/types/components/Button/LoadingIndicator/StyledLoadingIndicator.d.ts +0 -0
  42. package/types/components/Button/LoadingIndicator/index.d.ts +0 -0
  43. package/types/components/Button/StyledButton.d.ts +0 -0
  44. package/types/components/Button/UtilityButton/StyledUtilityButton.d.ts +0 -0
  45. package/types/components/Button/UtilityButton/index.d.ts +0 -0
  46. package/types/components/Button/index.d.ts +0 -0
  47. package/types/components/Calendar/CalendarRowItem.d.ts +0 -0
  48. package/types/components/Calendar/StyledCalendar.d.ts +0 -0
  49. package/types/components/Calendar/helpers.d.ts +0 -0
  50. package/types/components/Calendar/index.d.ts +0 -0
  51. package/types/components/Card/DataCard/StyledDataCard.d.ts +0 -0
  52. package/types/components/Card/DataCard/index.d.ts +0 -0
  53. package/types/components/Card/StyledCard.d.ts +0 -0
  54. package/types/components/Card/index.d.ts +0 -0
  55. package/types/components/Carousel/CardCarousel.d.ts +0 -0
  56. package/types/components/Carousel/CarouselItem.d.ts +0 -0
  57. package/types/components/Carousel/StyledCardCarousel.d.ts +0 -0
  58. package/types/components/Carousel/StyledCarousel.d.ts +0 -0
  59. package/types/components/Carousel/contants.d.ts +0 -0
  60. package/types/components/Carousel/index.d.ts +0 -0
  61. package/types/components/Carousel/types.d.ts +0 -0
  62. package/types/components/Checkbox/StyledCheckbox.d.ts +0 -0
  63. package/types/components/Checkbox/index.d.ts +0 -0
  64. package/types/components/Collapse/StyledCollapse.d.ts +0 -0
  65. package/types/components/Collapse/index.d.ts +0 -0
  66. package/types/components/ContentNavigator/StyledContentNavigator.d.ts +0 -0
  67. package/types/components/ContentNavigator/index.d.ts +0 -0
  68. package/types/components/DatePicker/DatePickerAndroid.d.ts +0 -0
  69. package/types/components/DatePicker/DatePickerIOS.d.ts +0 -0
  70. package/types/components/DatePicker/StyledDatePicker.d.ts +0 -0
  71. package/types/components/DatePicker/index.d.ts +0 -0
  72. package/types/components/DatePicker/types.d.ts +0 -0
  73. package/types/components/Divider/StyledDivider.d.ts +0 -0
  74. package/types/components/Divider/index.d.ts +0 -0
  75. package/types/components/Drawer/DragableDrawer/helpers.d.ts +0 -0
  76. package/types/components/Drawer/DragableDrawer/index.d.ts +0 -0
  77. package/types/components/Drawer/StyledDrawer.d.ts +0 -0
  78. package/types/components/Drawer/index.d.ts +0 -0
  79. package/types/components/Empty/StyledEmpty.d.ts +0 -0
  80. package/types/components/Empty/index.d.ts +0 -0
  81. package/types/components/FAB/ActionGroup/ActionItem.d.ts +0 -0
  82. package/types/components/FAB/ActionGroup/StyledActionGroup.d.ts +0 -0
  83. package/types/components/FAB/ActionGroup/StyledActionItem.d.ts +0 -0
  84. package/types/components/FAB/ActionGroup/index.d.ts +0 -0
  85. package/types/components/FAB/AnimatedFABIcon.d.ts +0 -0
  86. package/types/components/FAB/FAB.d.ts +0 -0
  87. package/types/components/FAB/StyledFAB.d.ts +0 -0
  88. package/types/components/FAB/index.d.ts +0 -0
  89. package/types/components/Icon/AnimatedIcon.d.ts +0 -0
  90. package/types/components/Icon/HeroIcon/index.d.ts +0 -0
  91. package/types/components/Icon/IconList.d.ts +0 -0
  92. package/types/components/Icon/index.d.ts +0 -0
  93. package/types/components/Icon/utils.d.ts +0 -0
  94. package/types/components/Image/index.d.ts +0 -0
  95. package/types/components/List/BasicListItem.d.ts +0 -0
  96. package/types/components/List/ListItem.d.ts +0 -0
  97. package/types/components/List/StyledBasicListItem.d.ts +0 -0
  98. package/types/components/List/StyledListItem.d.ts +0 -0
  99. package/types/components/List/index.d.ts +0 -0
  100. package/types/components/PageControl/StyledPageControl.d.ts +0 -0
  101. package/types/components/PageControl/index.d.ts +0 -0
  102. package/types/components/PinInput/PinCell.d.ts +0 -0
  103. package/types/components/PinInput/StyledPinInput.d.ts +0 -0
  104. package/types/components/PinInput/index.d.ts +0 -0
  105. package/types/components/Progress/ProgressBar.d.ts +0 -0
  106. package/types/components/Progress/ProgressCircle.d.ts +0 -0
  107. package/types/components/Progress/StyledProgressBar.d.ts +0 -0
  108. package/types/components/Progress/StyledProgressCircle.d.ts +0 -0
  109. package/types/components/Progress/__tests__/index.spec.d.ts +0 -0
  110. package/types/components/Progress/constants.d.ts +0 -0
  111. package/types/components/Progress/index.d.ts +0 -0
  112. package/types/components/Progress/types.d.ts +0 -0
  113. package/types/components/Radio/Radio.d.ts +0 -0
  114. package/types/components/Radio/RadioGroup.d.ts +0 -0
  115. package/types/components/Radio/StyledRadio.d.ts +0 -0
  116. package/types/components/Radio/index.d.ts +0 -0
  117. package/types/components/Radio/types.d.ts +0 -0
  118. package/types/components/RefreshControl/index.d.ts +0 -0
  119. package/types/components/RichTextEditor/EditorEvent.d.ts +0 -0
  120. package/types/components/RichTextEditor/EditorToolbar.d.ts +0 -0
  121. package/types/components/RichTextEditor/MentionList.d.ts +0 -0
  122. package/types/components/RichTextEditor/RichTextEditor.d.ts +0 -0
  123. package/types/components/RichTextEditor/StyledRichTextEditor.d.ts +0 -0
  124. package/types/components/RichTextEditor/StyledToolbar.d.ts +0 -0
  125. package/types/components/RichTextEditor/__mocks__/heroEditorApp.d.ts +0 -0
  126. package/types/components/RichTextEditor/constants.d.ts +0 -0
  127. package/types/components/RichTextEditor/heroEditorApp.d.ts +0 -0
  128. package/types/components/RichTextEditor/index.d.ts +0 -0
  129. package/types/components/RichTextEditor/types.d.ts +0 -0
  130. package/types/components/RichTextEditor/utils/events.d.ts +0 -0
  131. package/types/components/RichTextEditor/utils/rnWebView.d.ts +0 -0
  132. package/types/components/SectionHeading/StyledHeading.d.ts +0 -0
  133. package/types/components/SectionHeading/index.d.ts +0 -0
  134. package/types/components/Select/BaseOptionList.d.ts +0 -0
  135. package/types/components/Select/Footer.d.ts +0 -0
  136. package/types/components/Select/MultiSelect/Option.d.ts +0 -0
  137. package/types/components/Select/MultiSelect/OptionList.d.ts +0 -0
  138. package/types/components/Select/MultiSelect/index.d.ts +0 -0
  139. package/types/components/Select/SingleSelect/Option.d.ts +0 -0
  140. package/types/components/Select/SingleSelect/OptionList.d.ts +0 -0
  141. package/types/components/Select/SingleSelect/StyledSingleSelect.d.ts +0 -0
  142. package/types/components/Select/SingleSelect/index.d.ts +0 -0
  143. package/types/components/Select/StyledSelect.d.ts +0 -0
  144. package/types/components/Select/helpers.d.ts +0 -0
  145. package/types/components/Select/index.d.ts +0 -0
  146. package/types/components/Select/types.d.ts +0 -0
  147. package/types/components/Slider/index.d.ts +0 -0
  148. package/types/components/Spinner/AnimatedSpinner.d.ts +0 -0
  149. package/types/components/Spinner/StyledSpinner.d.ts +0 -0
  150. package/types/components/Spinner/index.d.ts +0 -0
  151. package/types/components/Swipeable/StyledSwipeable.d.ts +0 -0
  152. package/types/components/Swipeable/SwipeableAction.d.ts +0 -0
  153. package/types/components/Swipeable/index.d.ts +0 -0
  154. package/types/components/Switch/SelectorSwitch/Option.d.ts +0 -0
  155. package/types/components/Switch/SelectorSwitch/StyledSelectorSwitch.d.ts +0 -0
  156. package/types/components/Switch/SelectorSwitch/index.d.ts +0 -0
  157. package/types/components/Switch/StyledSwitch.d.ts +0 -0
  158. package/types/components/Switch/index.d.ts +0 -0
  159. package/types/components/Tabs/ActiveTabIndicator.d.ts +0 -0
  160. package/types/components/Tabs/ScrollableTabs.d.ts +8 -1
  161. package/types/components/Tabs/StyledScrollableTabs.d.ts +0 -0
  162. package/types/components/Tabs/StyledTabs.d.ts +0 -0
  163. package/types/components/Tabs/TabWithBadge.d.ts +0 -0
  164. package/types/components/Tabs/index.d.ts +2 -1
  165. package/types/components/Tabs/utils.d.ts +0 -0
  166. package/types/components/Tag/StyledTag.d.ts +0 -0
  167. package/types/components/Tag/index.d.ts +0 -0
  168. package/types/components/TextInput/StyledTextInput.d.ts +0 -0
  169. package/types/components/TextInput/index.d.ts +0 -0
  170. package/types/components/TimePicker/StyledTimePicker.d.ts +0 -0
  171. package/types/components/TimePicker/TimePickerAndroid.d.ts +0 -0
  172. package/types/components/TimePicker/TimePickerIOS.d.ts +0 -0
  173. package/types/components/TimePicker/index.d.ts +0 -0
  174. package/types/components/TimePicker/types.d.ts +0 -0
  175. package/types/components/Toast/StyledToast.d.ts +0 -0
  176. package/types/components/Toast/Toast.d.ts +0 -0
  177. package/types/components/Toast/ToastContainer.d.ts +0 -0
  178. package/types/components/Toast/ToastContext.d.ts +0 -0
  179. package/types/components/Toast/ToastProvider.d.ts +0 -0
  180. package/types/components/Toast/index.d.ts +0 -0
  181. package/types/components/Toast/types.d.ts +0 -0
  182. package/types/components/Toolbar/StyledToolbar.d.ts +0 -0
  183. package/types/components/Toolbar/ToolbarGroup.d.ts +0 -0
  184. package/types/components/Toolbar/ToolbarItem.d.ts +0 -0
  185. package/types/components/Toolbar/index.d.ts +0 -0
  186. package/types/components/Typography/Text/StyledText.d.ts +0 -0
  187. package/types/components/Typography/Text/index.d.ts +0 -0
  188. package/types/components/Typography/index.d.ts +0 -0
  189. package/types/index.d.ts +0 -0
  190. package/types/testHelpers/renderWithTheme.d.ts +0 -0
  191. package/types/theme/ThemeProvider.d.ts +0 -0
  192. package/types/theme/ThemeSwitcher.d.ts +0 -0
  193. package/types/theme/components/accordion.d.ts +0 -0
  194. package/types/theme/components/alert.d.ts +0 -0
  195. package/types/theme/components/attachment.d.ts +0 -0
  196. package/types/theme/components/avatar.d.ts +0 -0
  197. package/types/theme/components/badge.d.ts +0 -0
  198. package/types/theme/components/bottomNavigation.d.ts +0 -0
  199. package/types/theme/components/bottomSheet.d.ts +0 -0
  200. package/types/theme/components/button.d.ts +0 -0
  201. package/types/theme/components/calendar.d.ts +0 -0
  202. package/types/theme/components/card.d.ts +0 -0
  203. package/types/theme/components/cardCarousel.d.ts +0 -0
  204. package/types/theme/components/carousel.d.ts +0 -0
  205. package/types/theme/components/checkbox.d.ts +0 -0
  206. package/types/theme/components/contentNavigator.d.ts +0 -0
  207. package/types/theme/components/datePicker.d.ts +0 -0
  208. package/types/theme/components/divider.d.ts +0 -0
  209. package/types/theme/components/drawer.d.ts +0 -0
  210. package/types/theme/components/empty.d.ts +0 -0
  211. package/types/theme/components/fab.d.ts +0 -0
  212. package/types/theme/components/icon.d.ts +0 -0
  213. package/types/theme/components/image.d.ts +0 -0
  214. package/types/theme/components/list.d.ts +0 -0
  215. package/types/theme/components/pageControl.d.ts +0 -0
  216. package/types/theme/components/pinInput.d.ts +0 -0
  217. package/types/theme/components/progress.d.ts +0 -0
  218. package/types/theme/components/radio.d.ts +0 -0
  219. package/types/theme/components/refreshControl.d.ts +0 -0
  220. package/types/theme/components/richTextEditor.d.ts +0 -0
  221. package/types/theme/components/sectionHeading.d.ts +0 -0
  222. package/types/theme/components/select.d.ts +0 -0
  223. package/types/theme/components/slider.d.ts +0 -0
  224. package/types/theme/components/spinner.d.ts +0 -0
  225. package/types/theme/components/swipeable.d.ts +0 -0
  226. package/types/theme/components/switch.d.ts +0 -0
  227. package/types/theme/components/tabs.d.ts +0 -0
  228. package/types/theme/components/tag.d.ts +0 -0
  229. package/types/theme/components/textInput.d.ts +0 -0
  230. package/types/theme/components/timePicker.d.ts +0 -0
  231. package/types/theme/components/toast.d.ts +0 -0
  232. package/types/theme/components/toolbar.d.ts +0 -0
  233. package/types/theme/components/typography.d.ts +0 -0
  234. package/types/theme/getTheme.d.ts +0 -0
  235. package/types/theme/global/borders.d.ts +0 -0
  236. package/types/theme/global/colors/eBens.d.ts +0 -0
  237. package/types/theme/global/colors/global.d.ts +0 -0
  238. package/types/theme/global/colors/globalDark.d.ts +0 -0
  239. package/types/theme/global/colors/jobs.d.ts +0 -0
  240. package/types/theme/global/colors/swag.d.ts +0 -0
  241. package/types/theme/global/colors/swagDark.d.ts +0 -0
  242. package/types/theme/global/colors/types.d.ts +0 -0
  243. package/types/theme/global/colors/wallet.d.ts +0 -0
  244. package/types/theme/global/colors/work.d.ts +0 -0
  245. package/types/theme/global/index.d.ts +0 -0
  246. package/types/theme/global/scale.d.ts +0 -0
  247. package/types/theme/global/sizes.d.ts +0 -0
  248. package/types/theme/global/space.d.ts +0 -0
  249. package/types/theme/global/typography.d.ts +0 -0
  250. package/types/theme/index.d.ts +0 -0
  251. package/types/types.d.ts +2 -1
  252. package/types/utils/functions.d.ts +0 -0
  253. package/types/utils/helpers.d.ts +0 -0
  254. package/types/utils/hooks.d.ts +0 -0
  255. package/types/utils/scale.d.ts +0 -0
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { forwardRef } from 'react';
2
2
  import { TouchableWithoutFeedback, FlatList, Animated } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
  import { useTheme } from '@emotion/react';
@@ -19,6 +19,11 @@ import Icon from '../Icon';
19
19
  import Typography from '../Typography';
20
20
  import type { ItemType, TabsProps, TabType } from '.';
21
21
  import TabWithBadge from './TabWithBadge';
22
+ import { noop } from '../../utils/functions';
23
+
24
+ export type ScrollableTabsHandles = {
25
+ selectTabKey: (tabKey: string) => void;
26
+ };
22
27
 
23
28
  const getTabItem = ({
24
29
  item,
@@ -55,179 +60,215 @@ const getTabItem = ({
55
60
  return item({ color });
56
61
  };
57
62
 
58
- const ScrollableTab = ({
59
- onTabPress,
60
- selectedTabKey,
61
- tabs,
62
- containerStyle,
63
- barStyle,
64
- lazy = false,
65
- lazyPreloadDistance = 1,
66
- swipeEnabled = true,
67
- testID: componentTestID,
68
- }: TabsProps) => {
69
- const flatListRef = React.useRef<FlatList>(null);
70
- const pagerViewRef = React.useRef<PagerView>(null);
71
- const insets = useSafeAreaInsets();
72
- const theme = useTheme();
73
- const selectedTabIndex = tabs.findIndex(
74
- (item) => item.key === selectedTabKey
75
- );
76
- const tabsAnims = useAnimatedValueArray(
77
- tabs.map((_, i) => (i === selectedTabIndex ? 1 : 0))
78
- );
79
-
80
- React.useEffect(() => {
81
- let timeoutHandle: number | null = null;
82
- if (selectedTabIndex !== -1) {
83
- flatListRef.current?.scrollToIndex({
84
- index: selectedTabIndex,
85
- viewPosition: 0.5,
86
- });
87
- // If the selected tab is changed too quickly, the setPage is crashed and not work anymore
88
- // We apply throttle to prevent this issue https://github.com/Thinkei/hero-design/issues/1715
89
- timeoutHandle = setTimeout(() => {
90
- // use no animation to prevent unexpected behavior if users select tab too quickly
91
- pagerViewRef.current?.setPageWithoutAnimation(selectedTabIndex);
92
- }, 200);
93
- }
94
-
95
- const animation = Animated.parallel([
96
- ...tabs.map((_, i) =>
97
- Animated.timing(tabsAnims[i], {
98
- toValue: i === selectedTabIndex ? 1 : 0,
99
- duration: 150,
100
- useNativeDriver: true,
101
- })
102
- ),
103
- ]);
104
- animation.start();
105
-
106
- return () => {
107
- animation.stop();
108
- if (timeoutHandle) {
109
- clearTimeout(timeoutHandle);
63
+ export interface ScrollableTabsProps extends TabsProps {
64
+ ref?: React.Ref<ScrollableTabsHandles>;
65
+ }
66
+
67
+ const ScrollableTab = forwardRef<ScrollableTabsHandles, ScrollableTabsProps>(
68
+ (
69
+ {
70
+ onTabPress = noop,
71
+ selectedTabKey,
72
+ tabs,
73
+ containerStyle,
74
+ barStyle,
75
+ lazy = false,
76
+ lazyPreloadDistance = 1,
77
+ swipeEnabled = true,
78
+ testID: componentTestID,
79
+ },
80
+ ref?
81
+ ) => {
82
+ const flatListRef = React.useRef<FlatList>(null);
83
+ const pagerViewRef = React.useRef<PagerView>(null);
84
+ const insets = useSafeAreaInsets();
85
+ const theme = useTheme();
86
+ const selectedTabIndex = tabs.findIndex(
87
+ (item) => item.key === selectedTabKey
88
+ );
89
+ const tabsAnims = useAnimatedValueArray(
90
+ tabs.map((_, i) => (i === selectedTabIndex ? 1 : 0))
91
+ );
92
+
93
+ React.useImperativeHandle(
94
+ ref,
95
+ () => ({
96
+ selectTabKey: (tabKey: string) => {
97
+ const index = tabs.findIndex((item) => item.key === tabKey);
98
+ if (index !== -1) {
99
+ flatListRef.current?.scrollToIndex({
100
+ index,
101
+ viewPosition: 0.5,
102
+ });
103
+ pagerViewRef.current?.setPage(index);
104
+ if (onTabPress) {
105
+ onTabPress(tabKey);
106
+ }
107
+ }
108
+ },
109
+ getFlatListRef: () => {
110
+ return flatListRef.current;
111
+ },
112
+ getPagerViewRef: () => {
113
+ return pagerViewRef.current;
114
+ },
115
+ }),
116
+
117
+ [pagerViewRef, flatListRef, tabs]
118
+ );
119
+
120
+ React.useEffect(() => {
121
+ let timeoutHandle: number | null = null;
122
+ if (selectedTabIndex !== -1) {
123
+ flatListRef.current?.scrollToIndex({
124
+ index: selectedTabIndex,
125
+ viewPosition: 0.5,
126
+ });
127
+ // If the selected tab is changed too quickly, the setPage is crashed and not work anymore
128
+ // We apply throttle to prevent this issue https://github.com/Thinkei/hero-design/issues/1715
129
+ timeoutHandle = setTimeout(() => {
130
+ // use no animation to prevent unexpected behavior if users select tab too quickly
131
+ pagerViewRef.current?.setPageWithoutAnimation(selectedTabIndex);
132
+ }, 200);
110
133
  }
111
- };
112
- }, [selectedTabIndex, pagerViewRef]);
113
-
114
- return (
115
- <TabContainer style={containerStyle} testID={componentTestID}>
116
- <HeaderTabWrapper themeInsets={insets} style={barStyle}>
117
- <FlatList<TabType>
118
- testID={componentTestID ? `${componentTestID}-tab-bar` : undefined}
119
- ref={flatListRef}
120
- horizontal
121
- data={tabs}
122
- keyExtractor={(tab) => String(tab.key)}
123
- showsHorizontalScrollIndicator={false}
124
- onScrollToIndexFailed={({ index }) => {
125
- setTimeout(
126
- () =>
127
- flatListRef.current?.scrollToIndex({
128
- index,
129
- viewPosition: 0.5,
130
- }),
131
- 100
132
- );
133
- }}
134
- contentContainerStyle={{
135
- paddingHorizontal:
136
- theme.__hd__.tabs.space.flatListHorizontalPadding,
134
+
135
+ const animation = Animated.parallel([
136
+ ...tabs.map((_, i) =>
137
+ Animated.timing(tabsAnims[i], {
138
+ toValue: i === selectedTabIndex ? 1 : 0,
139
+ duration: 150,
140
+ useNativeDriver: true,
141
+ })
142
+ ),
143
+ ]);
144
+ animation.start();
145
+
146
+ return () => {
147
+ animation.stop();
148
+ if (timeoutHandle) {
149
+ clearTimeout(timeoutHandle);
150
+ }
151
+ };
152
+ }, [selectedTabIndex, selectedTabKey, pagerViewRef]);
153
+
154
+ return (
155
+ <TabContainer style={containerStyle} testID={componentTestID}>
156
+ <HeaderTabWrapper themeInsets={insets} style={barStyle}>
157
+ <FlatList<TabType>
158
+ testID={componentTestID ? `${componentTestID}-tab-bar` : undefined}
159
+ ref={flatListRef}
160
+ horizontal
161
+ data={tabs}
162
+ keyExtractor={(tab) => String(tab.key)}
163
+ showsHorizontalScrollIndicator={false}
164
+ onScrollToIndexFailed={({ index }) => {
165
+ setTimeout(
166
+ () =>
167
+ flatListRef.current?.scrollToIndex({
168
+ index,
169
+ viewPosition: 0.5,
170
+ }),
171
+ 100
172
+ );
173
+ }}
174
+ contentContainerStyle={{
175
+ paddingHorizontal:
176
+ theme.__hd__.tabs.space.flatListHorizontalPadding,
177
+ }}
178
+ renderItem={({ item: tab, index }) => {
179
+ const {
180
+ key,
181
+ testID,
182
+ activeItem,
183
+ inactiveItem: originalInactiveItem,
184
+ badge,
185
+ } = tab;
186
+ const active = selectedTabKey === key;
187
+ const activeAnimated = tabsAnims[index];
188
+ const outlineScale = active
189
+ ? activeAnimated.interpolate({
190
+ inputRange: [0, 1],
191
+ outputRange: [0.5, 1],
192
+ })
193
+ : 0;
194
+
195
+ const inactiveItem = originalInactiveItem ?? activeItem;
196
+ const tabItem = getTabItem({
197
+ item: active ? activeItem : inactiveItem,
198
+ color: active
199
+ ? theme.__hd__.tabs.colors.active
200
+ : theme.__hd__.tabs.colors.inactive,
201
+ active,
202
+ });
203
+
204
+ return (
205
+ <TouchableWithoutFeedback
206
+ key={key}
207
+ onPress={() => {
208
+ onTabPress(key);
209
+ }}
210
+ testID={testID}
211
+ >
212
+ <HeaderTabItem isFirstItem={index === 0}>
213
+ <HeaderTabItemOutlineWrapper>
214
+ <HeaderTabItemOutline
215
+ themeActive={active}
216
+ style={{
217
+ flex: 1,
218
+ transform: [
219
+ {
220
+ scaleX: outlineScale,
221
+ },
222
+ ],
223
+ }}
224
+ />
225
+ </HeaderTabItemOutlineWrapper>
226
+ <HeaderTabItemWrapper>
227
+ <TabWithBadge config={badge} tabItem={tabItem} />
228
+ </HeaderTabItemWrapper>
229
+ </HeaderTabItem>
230
+ </TouchableWithoutFeedback>
231
+ );
232
+ }}
233
+ />
234
+ </HeaderTabWrapper>
235
+ <ContentWrapper
236
+ initialPage={selectedTabIndex}
237
+ ref={pagerViewRef}
238
+ onPageSelected={(e) => {
239
+ const index = e.nativeEvent.position;
240
+ const selectedItem = tabs[index];
241
+ if (selectedItem && selectedItem.key !== selectedTabKey) {
242
+ onTabPress(selectedItem.key);
243
+ }
137
244
  }}
138
- renderItem={({ item: tab, index }) => {
139
- const {
140
- key,
141
- testID,
142
- activeItem,
143
- inactiveItem: originalInactiveItem,
144
- badge,
145
- } = tab;
245
+ scrollEnabled={swipeEnabled}
246
+ >
247
+ {tabs.map((tab, index) => {
248
+ const { key, component, testID } = tab;
146
249
  const active = selectedTabKey === key;
147
- const activeAnimated = tabsAnims[index];
148
- const outlineScale = active
149
- ? activeAnimated.interpolate({
150
- inputRange: [0, 1],
151
- outputRange: [0.5, 1],
152
- })
153
- : 0;
154
-
155
- const inactiveItem = originalInactiveItem ?? activeItem;
156
- const tabItem = getTabItem({
157
- item: active ? activeItem : inactiveItem,
158
- color: active
159
- ? theme.__hd__.tabs.colors.active
160
- : theme.__hd__.tabs.colors.inactive,
161
- active,
162
- });
250
+ const isLazyScreen =
251
+ lazy && Math.abs(selectedTabIndex - index) > lazyPreloadDistance;
163
252
 
164
253
  return (
165
- <TouchableWithoutFeedback
254
+ <TabScreen
166
255
  key={key}
167
- onPress={() => {
168
- onTabPress(key);
169
- }}
170
- testID={testID}
256
+ testID={testID ? `tab-screen-${testID}` : undefined}
257
+ pointerEvents={active ? 'auto' : 'none'}
258
+ accessibilityElementsHidden={!active}
259
+ importantForAccessibility={
260
+ active ? 'auto' : 'no-hide-descendants'
261
+ }
262
+ collapsable={false}
171
263
  >
172
- <HeaderTabItem isFirstItem={index === 0}>
173
- <HeaderTabItemOutlineWrapper>
174
- <HeaderTabItemOutline
175
- themeActive={active}
176
- style={{
177
- flex: 1,
178
- transform: [
179
- {
180
- scaleX: outlineScale,
181
- },
182
- ],
183
- }}
184
- />
185
- </HeaderTabItemOutlineWrapper>
186
- <HeaderTabItemWrapper>
187
- <TabWithBadge config={badge} tabItem={tabItem} />
188
- </HeaderTabItemWrapper>
189
- </HeaderTabItem>
190
- </TouchableWithoutFeedback>
264
+ {isLazyScreen ? null : component}
265
+ </TabScreen>
191
266
  );
192
- }}
193
- />
194
- </HeaderTabWrapper>
195
- <ContentWrapper
196
- initialPage={selectedTabIndex}
197
- ref={pagerViewRef}
198
- onPageSelected={(e) => {
199
- const index = e.nativeEvent.position;
200
- const selectedItem = tabs[index];
201
- if (selectedItem) {
202
- onTabPress(selectedItem.key);
203
- }
204
- }}
205
- scrollEnabled={swipeEnabled}
206
- >
207
- {tabs.map((tab, index) => {
208
- const { key, component, testID } = tab;
209
- const active = selectedTabKey === key;
210
- const isLazyScreen =
211
- lazy && Math.abs(selectedTabIndex - index) > lazyPreloadDistance;
212
-
213
- return (
214
- <TabScreen
215
- key={key}
216
- testID={testID ? `tab-screen-${testID}` : undefined}
217
- pointerEvents={active ? 'auto' : 'none'}
218
- accessibilityElementsHidden={!active}
219
- importantForAccessibility={
220
- active ? 'auto' : 'no-hide-descendants'
221
- }
222
- collapsable={false}
223
- >
224
- {isLazyScreen ? null : component}
225
- </TabScreen>
226
- );
227
- })}
228
- </ContentWrapper>
229
- </TabContainer>
230
- );
231
- };
267
+ })}
268
+ </ContentWrapper>
269
+ </TabContainer>
270
+ );
271
+ }
272
+ );
232
273
 
233
274
  export default ScrollableTab;
@@ -1,19 +1,24 @@
1
1
  import React from 'react';
2
- import { Text } from 'react-native';
3
- import { fireEvent } from '@testing-library/react-native';
2
+ import { FlatList, Text } from 'react-native';
3
+ import { fireEvent, waitFor } from '@testing-library/react-native';
4
4
  import { SafeAreaProvider } from 'react-native-safe-area-context';
5
- import type { ComponentProps } from 'react';
5
+ import PagerView from 'react-native-pager-view';
6
6
  import renderWithTheme from '../../../testHelpers/renderWithTheme';
7
7
  import Tabs from '..';
8
8
  import Button from '../../Button';
9
9
  import type { TabType } from '..';
10
+ import { ScrollableTabsProps, ScrollableTabsHandles } from '../ScrollableTabs';
10
11
 
11
- const TestTabsComponent = (
12
- props: Omit<
13
- ComponentProps<typeof Tabs>,
14
- 'selectedTabKey' | 'onTabPress' | 'tabs'
15
- >
16
- ) => {
12
+ type TestTabsProps = Omit<
13
+ ScrollableTabsProps,
14
+ 'selectedTabKey' | 'onTabPress' | 'tabs'
15
+ > & {
16
+ onTabPress?: (tabKey: string) => void;
17
+ };
18
+ const TestTabsComponent = React.forwardRef<
19
+ ScrollableTabsHandles,
20
+ TestTabsProps
21
+ >((props, ref) => {
17
22
  const [selectedTabKey, setSelectedTabKey] = React.useState('work');
18
23
  const tabs: TabType[] = [
19
24
  {
@@ -61,14 +66,20 @@ const TestTabsComponent = (
61
66
  icon="sync"
62
67
  />
63
68
  <Tabs.Scroll
69
+ ref={ref}
64
70
  {...props}
65
71
  tabs={tabs}
66
72
  selectedTabKey={selectedTabKey}
67
- onTabPress={(newTabKey) => setSelectedTabKey(newTabKey)}
73
+ onTabPress={(newTabKey) => {
74
+ setSelectedTabKey(newTabKey);
75
+ if (props.onTabPress) {
76
+ props.onTabPress(newTabKey);
77
+ }
78
+ }}
68
79
  />
69
80
  </SafeAreaProvider>
70
81
  );
71
- };
82
+ });
72
83
 
73
84
  describe('Tabs.Scroll', () => {
74
85
  it('renders correctly', () => {
@@ -106,4 +117,40 @@ describe('Tabs.Scroll', () => {
106
117
  expect(getByText('Home Screen')).toBeDefined();
107
118
  expect(queryByText('Work Screen')).toBeNull();
108
119
  });
120
+
121
+ describe('selectTabKey', () => {
122
+ it('should render correctly', () => {
123
+ const ref = React.createRef<
124
+ ScrollableTabsHandles & {
125
+ getFlatListRef: () => FlatList;
126
+ getPagerViewRef: () => PagerView;
127
+ }
128
+ >();
129
+ const onTabPressSpye = jest.fn();
130
+ renderWithTheme(
131
+ <TestTabsComponent
132
+ ref={ref}
133
+ lazy
134
+ lazyPreloadDistance={1}
135
+ onTabPress={onTabPressSpye}
136
+ swipeEnabled={false}
137
+ />
138
+ );
139
+
140
+ const flatListRef = ref.current!.getFlatListRef()!;
141
+ const pagerViewRef = ref.current!.getPagerViewRef()!;
142
+ const scrollToIndexSpy = jest.spyOn(flatListRef, 'scrollToIndex');
143
+ const setPageSpy = jest.spyOn(pagerViewRef, 'setPage');
144
+ ref.current?.selectTabKey('personal');
145
+ waitFor(() => expect(scrollToIndexSpy).toHaveBeenCalled());
146
+ waitFor(() => expect(setPageSpy).toHaveBeenCalled());
147
+
148
+ expect(scrollToIndexSpy).toHaveBeenCalledWith({
149
+ index: 1,
150
+ viewPosition: 0.5,
151
+ });
152
+ expect(setPageSpy).toHaveBeenCalledWith(1);
153
+ expect(onTabPressSpye).toHaveBeenCalledWith('personal');
154
+ });
155
+ });
109
156
  });
package/src/types.ts CHANGED
@@ -15,6 +15,7 @@ import type {
15
15
  import { SwipeableProps } from './components/Swipeable';
16
16
  import { TextProps } from './components/Typography/Text';
17
17
  import { CardCarouselHandles } from './components/Carousel/CardCarousel';
18
+ import { ScrollableTabsHandles } from './components/Tabs/ScrollableTabs';
18
19
 
19
20
  export type {
20
21
  BottomNavigationTabType,
@@ -27,6 +28,7 @@ export type {
27
28
  RichTextEditorProps,
28
29
  RichTextEditorRef,
29
30
  TabType,
31
+ ScrollableTabsHandles,
30
32
  TextInputProps,
31
33
  TextProps,
32
34
  TextInputHandles,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes