@hero-design/rn 8.130.2 → 8.131.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.
- package/CHANGELOG.md +12 -0
- package/es/index.js +407 -225
- package/lib/index.js +407 -225
- package/package.json +1 -1
- package/src/components/Tabs/ScrollableTabsHeader/ScrollableTabsHeader.tsx +217 -131
- package/src/components/Tabs/ScrollableTabsHeader/hooks/useIndicatorAnimation.ts +242 -0
- package/src/components/Tabs/StyledScrollableTabs.tsx +68 -21
- package/src/components/Tabs/index.tsx +2 -0
- package/src/theme/components/tabs.ts +9 -2
- package/src/theme/global/colors/ehJobs.ts +1 -0
- package/src/theme/global/colors/ehWork.ts +1 -1
- package/types/components/Checkbox/StyledInlineCheckBox.d.ts +1 -1
- package/types/components/Tabs/ScrollableTabsHeader/ScrollableTabsHeader.d.ts +1 -1
- package/types/components/Tabs/ScrollableTabsHeader/hooks/useIndicatorAnimation.d.ts +75 -0
- package/types/components/Tabs/StyledScrollableTabs.d.ts +13 -8
- package/types/components/Tabs/index.d.ts +3 -1
- package/types/theme/components/tabs.d.ts +9 -2
- package/src/components/Tabs/ScrollableTabsHeader/hooks/useInitHighlightedAnimation.ts +0 -45
- package/types/components/Tabs/ScrollableTabsHeader/hooks/useInitHighlightedAnimation.d.ts +0 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Animated, Platform,
|
|
1
|
+
import { Animated, Platform, View } from 'react-native';
|
|
2
2
|
import styled from '@emotion/native';
|
|
3
3
|
|
|
4
4
|
// Checks if the platform is Android 7x and 8.x, i.e., API levels 24 to 27 (Android 7.0 to 8.1),
|
|
@@ -22,30 +22,75 @@ const HeaderTabWrapper = styled(View)<{
|
|
|
22
22
|
backgroundColor: theme.__hd__.tabs.colors.headerBackground,
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)
|
|
25
|
+
const getItemMarginLeft = (
|
|
26
|
+
isFirstItem: boolean | undefined,
|
|
27
|
+
themeVariant: 'underlined' | 'highlighted' | undefined,
|
|
28
|
+
highlightedMargin: number,
|
|
29
|
+
defaultMargin: number
|
|
30
|
+
) => {
|
|
31
|
+
if (isFirstItem) return 0;
|
|
32
|
+
if (themeVariant === 'highlighted') return highlightedMargin;
|
|
33
|
+
return defaultMargin;
|
|
34
|
+
};
|
|
31
35
|
|
|
32
|
-
const
|
|
36
|
+
const HeaderTabItem = styled(Animated.View)<{
|
|
37
|
+
isFirstItem?: boolean;
|
|
38
|
+
themeVariant?: 'underlined' | 'highlighted';
|
|
39
|
+
}>(({ theme, isFirstItem, themeVariant }) => ({
|
|
40
|
+
marginLeft: getItemMarginLeft(
|
|
41
|
+
isFirstItem,
|
|
42
|
+
themeVariant,
|
|
43
|
+
theme.__hd__.tabs.space.highlightedItemMargin,
|
|
44
|
+
theme.__hd__.tabs.space.itemMargin
|
|
45
|
+
),
|
|
33
46
|
paddingVertical: theme.__hd__.tabs.space.itemVerticalPadding,
|
|
34
|
-
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
justifyContent: 'center',
|
|
35
49
|
}));
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// Three-piece pill: left cap + body + right cap, all native-driver animated.
|
|
52
|
+
// Splitting into pieces keeps border-radius on fixed-width views so scaleX
|
|
53
|
+
// never distorts the rounded corners. Cap width equals the border-radius so
|
|
54
|
+
// the rounded edge is fully contained within the cap piece.
|
|
55
|
+
const HeaderTabPillLeft = styled(Animated.View)(({ theme }) => ({
|
|
56
|
+
position: 'absolute',
|
|
57
|
+
top: 0,
|
|
58
|
+
bottom: 0,
|
|
59
|
+
left: 0,
|
|
60
|
+
width: theme.__hd__.tabs.radii.highlightedOutline,
|
|
61
|
+
borderTopLeftRadius: theme.__hd__.tabs.radii.highlightedOutline,
|
|
62
|
+
backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBackground,
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
const HeaderTabPillBody = styled(Animated.View)(({ theme }) => ({
|
|
66
|
+
position: 'absolute',
|
|
67
|
+
top: 0,
|
|
68
|
+
bottom: 0,
|
|
69
|
+
left: 0,
|
|
70
|
+
width: 1,
|
|
71
|
+
backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBackground,
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
const HeaderTabPillRight = styled(Animated.View)(({ theme }) => ({
|
|
75
|
+
position: 'absolute',
|
|
76
|
+
top: 0,
|
|
77
|
+
bottom: 0,
|
|
78
|
+
left: 0,
|
|
79
|
+
width: theme.__hd__.tabs.radii.highlightedOutline,
|
|
80
|
+
borderTopRightRadius: theme.__hd__.tabs.radii.highlightedOutline,
|
|
81
|
+
backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBackground,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
const HeaderTabItemActiveBorder = styled(Animated.View)(({ theme }) => ({
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
bottom: 0,
|
|
87
|
+
width: 1,
|
|
88
|
+
height: theme.__hd__.tabs.borderWidths.highlightedActiveBorder,
|
|
89
|
+
backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBorder,
|
|
90
|
+
}));
|
|
45
91
|
|
|
46
92
|
const HeaderTabItemWrapper = styled(View)(({ theme }) => ({
|
|
47
93
|
paddingHorizontal: theme.__hd__.tabs.space.outlineHorizontalPadding,
|
|
48
|
-
paddingVertical: theme.__hd__.tabs.space.outlineVerticalPadding,
|
|
49
94
|
position: 'relative',
|
|
50
95
|
justifyContent: 'center',
|
|
51
96
|
...(!isAndroid8 && {
|
|
@@ -54,7 +99,7 @@ const HeaderTabItemWrapper = styled(View)(({ theme }) => ({
|
|
|
54
99
|
}));
|
|
55
100
|
|
|
56
101
|
const HeaderTabItemIndicator = styled(Animated.View)(({ theme }) => ({
|
|
57
|
-
width:
|
|
102
|
+
width: 1,
|
|
58
103
|
height: theme.__hd__.tabs.sizes.indicator,
|
|
59
104
|
position: 'absolute',
|
|
60
105
|
bottom: theme.__hd__.tabs.space.tabIndicatorBottom,
|
|
@@ -67,8 +112,10 @@ export {
|
|
|
67
112
|
TabScreen,
|
|
68
113
|
TabContainer,
|
|
69
114
|
HeaderTabWrapper,
|
|
70
|
-
|
|
71
|
-
|
|
115
|
+
HeaderTabPillLeft,
|
|
116
|
+
HeaderTabPillBody,
|
|
117
|
+
HeaderTabPillRight,
|
|
118
|
+
HeaderTabItemActiveBorder,
|
|
72
119
|
HeaderTabItemWrapper,
|
|
73
120
|
HeaderTabItemIndicator,
|
|
74
121
|
};
|
|
@@ -9,6 +9,10 @@ const getTabsTheme = (theme: GlobalTheme) => {
|
|
|
9
9
|
indicator: theme.colors.primary,
|
|
10
10
|
text: theme.colors.onDefaultGlobalSurface,
|
|
11
11
|
headerBackground: theme.colors.defaultGlobalSurface,
|
|
12
|
+
highlightedActiveText: theme.colors.primary,
|
|
13
|
+
highlightedActiveBorder: theme.colors.primary,
|
|
14
|
+
highlightedActiveBackground: theme.colors.neutralGlobalSurface,
|
|
15
|
+
highlightedDisabledText: theme.colors.disabledOnDefaultGlobalSurface,
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
const space = {
|
|
@@ -17,16 +21,19 @@ const getTabsTheme = (theme: GlobalTheme) => {
|
|
|
17
21
|
itemVerticalPadding: theme.space.small,
|
|
18
22
|
itemMargin: theme.space.smallMedium,
|
|
19
23
|
outlineHorizontalPadding: theme.space.small,
|
|
20
|
-
outlineVerticalPadding: theme.space.xsmall,
|
|
21
24
|
tabIndicatorBottom: -theme.space.xxsmall,
|
|
25
|
+
highlightedItemMargin: theme.space.xsmall,
|
|
26
|
+
highlightedBarTopPadding: theme.space.xxsmall,
|
|
22
27
|
};
|
|
23
28
|
|
|
24
29
|
const radii = {
|
|
25
|
-
|
|
30
|
+
highlightedOutline: theme.radii.medium,
|
|
26
31
|
};
|
|
27
32
|
|
|
28
33
|
const borderWidths = {
|
|
29
34
|
headerBottom: theme.borderWidths.medium,
|
|
35
|
+
highlightedHeaderBottom: theme.borderWidths.base,
|
|
36
|
+
highlightedActiveBorder: theme.borderWidths.base,
|
|
30
37
|
};
|
|
31
38
|
|
|
32
39
|
const sizes = {
|
|
@@ -3,7 +3,7 @@ import type { SystemPalette, BrandSystemPalette } from './types';
|
|
|
3
3
|
import swagLightGlobalPalette from './swagLightGlobal';
|
|
4
4
|
|
|
5
5
|
const ehWorkBrandSystemPalette: BrandSystemPalette = {
|
|
6
|
-
primary: '#
|
|
6
|
+
primary: '#7622d7',
|
|
7
7
|
onPrimary: '#fdfbff',
|
|
8
8
|
secondary: '#b382fd',
|
|
9
9
|
onSecondary: palette.white,
|
|
@@ -32,7 +32,7 @@ export declare const StyledCheckMark: import("@emotion/native").StyledComponent<
|
|
|
32
32
|
as?: React.ElementType;
|
|
33
33
|
} & {
|
|
34
34
|
themeSize: "small" | "xsmall" | "medium" | "large" | "xlarge" | "xxxsmall";
|
|
35
|
-
themeIntent: "
|
|
35
|
+
themeIntent: "primary" | "secondary" | "text" | "success" | "info" | "warning" | "danger" | "muted" | "inactive" | "disabled-text" | "text-inverted";
|
|
36
36
|
} & {
|
|
37
37
|
ref?: import("react").Ref<import("react-native-vector-icons/Icon").Icon> | undefined;
|
|
38
38
|
} & {
|
|
@@ -36,5 +36,5 @@ export interface ScrollableTabHeaderProps extends ViewProps {
|
|
|
36
36
|
*/
|
|
37
37
|
variant?: 'underlined' | 'highlighted';
|
|
38
38
|
}
|
|
39
|
-
declare const ScrollableTabHeader: ({ onTabPress, selectedIndex, tabs, barStyle, testID, insets, variant, }: ScrollableTabHeaderProps) => React.JSX.Element;
|
|
39
|
+
declare const ScrollableTabHeader: ({ onTabPress, selectedIndex: rawSelectedIndex, tabs, barStyle, testID, insets, variant, }: ScrollableTabHeaderProps) => React.JSX.Element;
|
|
40
40
|
export default ScrollableTabHeader;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Animated } from 'react-native';
|
|
2
|
+
import type { LayoutChangeEvent } from 'react-native';
|
|
3
|
+
/**
|
|
4
|
+
* Drives two visual layers that slide to the selected tab on every press:
|
|
5
|
+
*
|
|
6
|
+
* Layer 1 — bottom border / underline (indicatorStyle)
|
|
7
|
+
* ─────────────────────────────────────────────────────
|
|
8
|
+
* Uses the "width:1 + scaleX" trick: the element has a fixed stylesheet
|
|
9
|
+
* width of 1px and scaleX is set to the target pixel width, giving a visual
|
|
10
|
+
* width of 1 × scaleX pixels without touching any layout property.
|
|
11
|
+
* Both translateX and scaleX are transform properties → native driver.
|
|
12
|
+
* Caveat: scaleX also scales border-radius, so this layer has no border-radius.
|
|
13
|
+
*
|
|
14
|
+
* Layer 2 — pill background (pillLeftStyle / pillBodyStyle / pillRightStyle)
|
|
15
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
* The pill is split into three absolutely-positioned children so that
|
|
17
|
+
* border-radius is never distorted by scale:
|
|
18
|
+
*
|
|
19
|
+
* ┌──────────────────────────────────────────────────────┐
|
|
20
|
+
* │ [cap-left 8px] [body width-1 + scaleX] [cap-right 8px] │
|
|
21
|
+
* └──────────────────────────────────────────────────────┘
|
|
22
|
+
*
|
|
23
|
+
* cap-left — fixed 8px wide, borderTopLeftRadius:8, translateX = pillX
|
|
24
|
+
* body — width:1 + scaleX trick (scaleX = tabWidth - 16),
|
|
25
|
+
* transformOrigin 'left center',
|
|
26
|
+
* translateX = pillX + 8 (via Animated.add)
|
|
27
|
+
* cap-right — fixed 8px wide, borderTopRightRadius:8,
|
|
28
|
+
* translateX = pillX + tabWidth - 8 (via Animated.add)
|
|
29
|
+
*
|
|
30
|
+
* All four animated values use the native driver (translateX and scaleX are
|
|
31
|
+
* transform properties). `width` is never animated, so no JS driver needed.
|
|
32
|
+
*
|
|
33
|
+
* Driver summary:
|
|
34
|
+
* indicatorX native translateX — slides the bottom border
|
|
35
|
+
* indicatorScaleX native scaleX — stretches the bottom border
|
|
36
|
+
* pillX native translateX — slides all three pill pieces
|
|
37
|
+
* pillBodyScaleX native scaleX — stretches the pill body
|
|
38
|
+
* pillRightOffsetX native translateX — positions the right cap
|
|
39
|
+
* (Animated.add: pillX + tabWidth - 8)
|
|
40
|
+
*/
|
|
41
|
+
declare const useIndicatorAnimation: ({ selectedIndex, tabsLength, pillCapWidth, }: {
|
|
42
|
+
selectedIndex: number | undefined;
|
|
43
|
+
tabsLength: number;
|
|
44
|
+
/** Width of each rounded cap, should equal theme radii.highlightedOutline. */
|
|
45
|
+
pillCapWidth: number;
|
|
46
|
+
}) => {
|
|
47
|
+
indicatorStyle: {
|
|
48
|
+
readonly transformOrigin: "left center";
|
|
49
|
+
readonly transform: readonly [{
|
|
50
|
+
readonly translateX: Animated.Value;
|
|
51
|
+
}, {
|
|
52
|
+
readonly scaleX: Animated.Value;
|
|
53
|
+
}];
|
|
54
|
+
};
|
|
55
|
+
pillLeftStyle: {
|
|
56
|
+
readonly transform: readonly [{
|
|
57
|
+
readonly translateX: Animated.Value;
|
|
58
|
+
}];
|
|
59
|
+
};
|
|
60
|
+
pillBodyStyle: {
|
|
61
|
+
readonly transformOrigin: "left center";
|
|
62
|
+
readonly transform: readonly [{
|
|
63
|
+
readonly translateX: Animated.AnimatedAddition<string | number>;
|
|
64
|
+
}, {
|
|
65
|
+
readonly scaleX: Animated.Value;
|
|
66
|
+
}];
|
|
67
|
+
};
|
|
68
|
+
pillRightStyle: {
|
|
69
|
+
readonly transform: readonly [{
|
|
70
|
+
readonly translateX: Animated.AnimatedAddition<string | number>;
|
|
71
|
+
}];
|
|
72
|
+
};
|
|
73
|
+
onTabLayout: (index: number, event: LayoutChangeEvent) => void;
|
|
74
|
+
};
|
|
75
|
+
export default useIndicatorAnimation;
|
|
@@ -29,18 +29,23 @@ declare const HeaderTabItem: import("@emotion/native").StyledComponent<Animated.
|
|
|
29
29
|
as?: React.ElementType;
|
|
30
30
|
} & {
|
|
31
31
|
isFirstItem?: boolean;
|
|
32
|
+
themeVariant?: "underlined" | "highlighted";
|
|
32
33
|
}, {}, {}>;
|
|
33
|
-
declare const
|
|
34
|
+
declare const HeaderTabPillLeft: import("@emotion/native").StyledComponent<Animated.AnimatedProps<import("react-native").ViewProps & import("react").RefAttributes<View>> & {
|
|
34
35
|
theme?: import("@emotion/react").Theme;
|
|
35
36
|
as?: React.ElementType;
|
|
36
|
-
}, {}, {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
}, {}, {}>;
|
|
38
|
+
declare const HeaderTabPillBody: import("@emotion/native").StyledComponent<Animated.AnimatedProps<import("react-native").ViewProps & import("react").RefAttributes<View>> & {
|
|
39
|
+
theme?: import("@emotion/react").Theme;
|
|
40
|
+
as?: React.ElementType;
|
|
41
|
+
}, {}, {}>;
|
|
42
|
+
declare const HeaderTabPillRight: import("@emotion/native").StyledComponent<Animated.AnimatedProps<import("react-native").ViewProps & import("react").RefAttributes<View>> & {
|
|
43
|
+
theme?: import("@emotion/react").Theme;
|
|
44
|
+
as?: React.ElementType;
|
|
45
|
+
}, {}, {}>;
|
|
46
|
+
declare const HeaderTabItemActiveBorder: import("@emotion/native").StyledComponent<Animated.AnimatedProps<import("react-native").ViewProps & import("react").RefAttributes<View>> & {
|
|
40
47
|
theme?: import("@emotion/react").Theme;
|
|
41
48
|
as?: React.ElementType;
|
|
42
|
-
} & {
|
|
43
|
-
themeActive: boolean;
|
|
44
49
|
}, {}, {}>;
|
|
45
50
|
declare const HeaderTabItemWrapper: import("@emotion/native").StyledComponent<import("react-native").ViewProps & {
|
|
46
51
|
theme?: import("@emotion/react").Theme;
|
|
@@ -52,4 +57,4 @@ declare const HeaderTabItemIndicator: import("@emotion/native").StyledComponent<
|
|
|
52
57
|
theme?: import("@emotion/react").Theme;
|
|
53
58
|
as?: React.ElementType;
|
|
54
59
|
}, {}, {}>;
|
|
55
|
-
export { HeaderTabItem, TabScreen, TabContainer, HeaderTabWrapper,
|
|
60
|
+
export { HeaderTabItem, TabScreen, TabContainer, HeaderTabWrapper, HeaderTabPillLeft, HeaderTabPillBody, HeaderTabPillRight, HeaderTabItemActiveBorder, HeaderTabItemWrapper, HeaderTabItemIndicator, };
|
|
@@ -14,6 +14,8 @@ export type TabType = {
|
|
|
14
14
|
component: ReactNode;
|
|
15
15
|
testID?: string;
|
|
16
16
|
badge?: BadgeConfigType;
|
|
17
|
+
/** Whether the tab is disabled. Disabled tabs cannot be pressed. */
|
|
18
|
+
disabled?: boolean;
|
|
17
19
|
};
|
|
18
20
|
export interface TabsHeaderProps {
|
|
19
21
|
tabs: TabType[];
|
|
@@ -80,7 +82,7 @@ export interface TabsProps extends ViewProps {
|
|
|
80
82
|
declare const _default: (({ onTabPress, selectedTabKey, tabs, containerStyle, barStyle, lazy, lazyPreloadDistance, swipeEnabled, testID: componentTestID, header, }: TabsProps) => ReactElement) & {
|
|
81
83
|
Header: ({ tabs, selectedTabKey, onTabPress, barStyle, insets, componentTestID, tabsWidth, setTabsWidth, positionAnimatedValue, scrollOffsetAnimatedValue, }: TabsHeaderProps) => React.JSX.Element;
|
|
82
84
|
Scroll: ({ onTabPress, selectedTabKey, tabs, containerStyle, barStyle, lazy, lazyPreloadDistance, swipeEnabled, testID: componentTestID, variant, header, }: import("./ScrollableTabs").ScrollableTabProps) => React.JSX.Element;
|
|
83
|
-
ScrollHeader: ({ onTabPress, selectedIndex, tabs, barStyle, testID, insets, variant, }: import("./ScrollableTabsHeader/ScrollableTabsHeader").ScrollableTabHeaderProps) => React.JSX.Element;
|
|
85
|
+
ScrollHeader: ({ onTabPress, selectedIndex: rawSelectedIndex, tabs, barStyle, testID, insets, variant, }: import("./ScrollableTabsHeader/ScrollableTabsHeader").ScrollableTabHeaderProps) => React.JSX.Element;
|
|
84
86
|
useIsFocused: () => boolean | undefined;
|
|
85
87
|
};
|
|
86
88
|
export default _default;
|
|
@@ -2,6 +2,8 @@ import type { GlobalTheme } from '../global';
|
|
|
2
2
|
declare const getTabsTheme: (theme: GlobalTheme) => {
|
|
3
3
|
borderWidths: {
|
|
4
4
|
headerBottom: number;
|
|
5
|
+
highlightedHeaderBottom: number;
|
|
6
|
+
highlightedActiveBorder: number;
|
|
5
7
|
};
|
|
6
8
|
colors: {
|
|
7
9
|
active: string;
|
|
@@ -11,6 +13,10 @@ declare const getTabsTheme: (theme: GlobalTheme) => {
|
|
|
11
13
|
indicator: string;
|
|
12
14
|
text: string;
|
|
13
15
|
headerBackground: string;
|
|
16
|
+
highlightedActiveText: string;
|
|
17
|
+
highlightedActiveBorder: string;
|
|
18
|
+
highlightedActiveBackground: string;
|
|
19
|
+
highlightedDisabledText: string;
|
|
14
20
|
};
|
|
15
21
|
space: {
|
|
16
22
|
flatListHorizontalPadding: number;
|
|
@@ -18,11 +24,12 @@ declare const getTabsTheme: (theme: GlobalTheme) => {
|
|
|
18
24
|
itemVerticalPadding: number;
|
|
19
25
|
itemMargin: number;
|
|
20
26
|
outlineHorizontalPadding: number;
|
|
21
|
-
outlineVerticalPadding: number;
|
|
22
27
|
tabIndicatorBottom: number;
|
|
28
|
+
highlightedItemMargin: number;
|
|
29
|
+
highlightedBarTopPadding: number;
|
|
23
30
|
};
|
|
24
31
|
radii: {
|
|
25
|
-
|
|
32
|
+
highlightedOutline: number;
|
|
26
33
|
};
|
|
27
34
|
sizes: {
|
|
28
35
|
indicator: number;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Animated, Platform } from 'react-native';
|
|
3
|
-
import { useAnimatedValueArray } from '../../utils';
|
|
4
|
-
|
|
5
|
-
const useInitHighlightedAnimation = ({
|
|
6
|
-
selectedIndex = 0,
|
|
7
|
-
tabsLength,
|
|
8
|
-
variant,
|
|
9
|
-
}: {
|
|
10
|
-
selectedIndex?: number;
|
|
11
|
-
tabsLength: number;
|
|
12
|
-
variant: 'underlined' | 'highlighted';
|
|
13
|
-
}) => {
|
|
14
|
-
const tabsAnims = useAnimatedValueArray(
|
|
15
|
-
Array.from({ length: tabsLength }).map((_, i) =>
|
|
16
|
-
i === selectedIndex ? 1 : 0
|
|
17
|
-
)
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
React.useEffect(() => {
|
|
21
|
-
if (variant !== 'highlighted') {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const animation = Animated.parallel([
|
|
26
|
-
...Array.from({ length: tabsLength }).map((_, i) =>
|
|
27
|
-
Animated.timing(tabsAnims[i], {
|
|
28
|
-
toValue: i === selectedIndex ? 1 : 0,
|
|
29
|
-
duration: 150,
|
|
30
|
-
useNativeDriver: Platform.OS !== 'web',
|
|
31
|
-
})
|
|
32
|
-
),
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
animation.start();
|
|
36
|
-
|
|
37
|
-
return () => {
|
|
38
|
-
animation.stop();
|
|
39
|
-
};
|
|
40
|
-
}, [selectedIndex]);
|
|
41
|
-
|
|
42
|
-
return { tabsAnims };
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export default useInitHighlightedAnimation;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Animated } from 'react-native';
|
|
2
|
-
declare const useInitHighlightedAnimation: ({ selectedIndex, tabsLength, variant, }: {
|
|
3
|
-
selectedIndex?: number;
|
|
4
|
-
tabsLength: number;
|
|
5
|
-
variant: "underlined" | "highlighted";
|
|
6
|
-
}) => {
|
|
7
|
-
tabsAnims: Animated.Value[];
|
|
8
|
-
};
|
|
9
|
-
export default useInitHighlightedAnimation;
|