@oxyhq/bloom 0.35.2 → 0.35.4
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/lib/commonjs/tabs/Tabs.js +98 -14
- package/lib/commonjs/tabs/Tabs.js.map +1 -1
- package/lib/module/tabs/Tabs.js +100 -16
- package/lib/module/tabs/Tabs.js.map +1 -1
- package/lib/typescript/commonjs/tabs/Tabs.d.ts.map +1 -1
- package/lib/typescript/commonjs/tabs/types.d.ts +7 -0
- package/lib/typescript/commonjs/tabs/types.d.ts.map +1 -1
- package/lib/typescript/module/tabs/Tabs.d.ts.map +1 -1
- package/lib/typescript/module/tabs/types.d.ts +7 -0
- package/lib/typescript/module/tabs/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/Tabs.test.tsx +110 -0
- package/src/tabs/Tabs.tsx +121 -18
- package/src/tabs/types.ts +7 -0
|
@@ -14,22 +14,74 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
14
14
|
const TabsContext = /*#__PURE__*/(0, _react.createContext)({
|
|
15
15
|
value: '',
|
|
16
16
|
onValueChange: () => {},
|
|
17
|
-
variant: 'underline'
|
|
17
|
+
variant: 'underline',
|
|
18
|
+
fullWidth: false,
|
|
19
|
+
reportTriggerLayout: () => {}
|
|
18
20
|
});
|
|
19
21
|
const TabsBarComponent = ({
|
|
20
22
|
value,
|
|
21
23
|
onValueChange,
|
|
22
24
|
variant = 'underline',
|
|
25
|
+
fullWidth = false,
|
|
23
26
|
children,
|
|
24
27
|
style,
|
|
25
28
|
testID
|
|
26
29
|
}) => {
|
|
27
30
|
const theme = (0, _useTheme.useTheme)();
|
|
31
|
+
const isUnderline = variant === 'underline';
|
|
32
|
+
|
|
33
|
+
// Shared sliding underline: one indicator that translates + resizes between
|
|
34
|
+
// triggers. Triggers report their measured {x, width}; the container drives
|
|
35
|
+
// these Animated values imperatively (no re-render on measure or slide).
|
|
36
|
+
const indicatorX = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
37
|
+
const indicatorWidth = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
38
|
+
const triggerLayoutsRef = (0, _react.useRef)({});
|
|
39
|
+
const indicatorPlacedRef = (0, _react.useRef)(false);
|
|
40
|
+
const moveIndicator = (0, _react.useCallback)((target, animate) => {
|
|
41
|
+
if (animate) {
|
|
42
|
+
_reactNative.Animated.parallel([_reactNative.Animated.timing(indicatorX, {
|
|
43
|
+
toValue: target.x,
|
|
44
|
+
duration: 200,
|
|
45
|
+
easing: _reactNative.Easing.out(_reactNative.Easing.cubic),
|
|
46
|
+
useNativeDriver: false
|
|
47
|
+
}), _reactNative.Animated.timing(indicatorWidth, {
|
|
48
|
+
toValue: target.width,
|
|
49
|
+
duration: 200,
|
|
50
|
+
easing: _reactNative.Easing.out(_reactNative.Easing.cubic),
|
|
51
|
+
useNativeDriver: false
|
|
52
|
+
})]).start();
|
|
53
|
+
} else {
|
|
54
|
+
indicatorX.setValue(target.x);
|
|
55
|
+
indicatorWidth.setValue(target.width);
|
|
56
|
+
}
|
|
57
|
+
}, [indicatorX, indicatorWidth]);
|
|
58
|
+
const reportTriggerLayout = (0, _react.useCallback)((tabValue, layout) => {
|
|
59
|
+
triggerLayoutsRef.current[tabValue] = layout;
|
|
60
|
+
// Snap onto the active trigger the moment it's first measured (mount) or
|
|
61
|
+
// when its size changes — never slide in from the origin.
|
|
62
|
+
if (tabValue === value) {
|
|
63
|
+
moveIndicator(layout, false);
|
|
64
|
+
indicatorPlacedRef.current = true;
|
|
65
|
+
}
|
|
66
|
+
}, [value, moveIndicator]);
|
|
67
|
+
|
|
68
|
+
// Slide the indicator to the newly-selected trigger. Syncing an imperative
|
|
69
|
+
// Animated value to the controlled `value` prop is a legitimate effect
|
|
70
|
+
// (external-system sync); the first placement snaps, later changes animate.
|
|
71
|
+
(0, _react.useEffect)(() => {
|
|
72
|
+
if (!isUnderline) return;
|
|
73
|
+
const target = triggerLayoutsRef.current[value];
|
|
74
|
+
if (!target) return; // not measured yet — reportTriggerLayout will place it
|
|
75
|
+
moveIndicator(target, indicatorPlacedRef.current);
|
|
76
|
+
indicatorPlacedRef.current = true;
|
|
77
|
+
}, [value, isUnderline, moveIndicator]);
|
|
28
78
|
const contextValue = (0, _react.useMemo)(() => ({
|
|
29
79
|
value,
|
|
30
80
|
onValueChange,
|
|
31
|
-
variant
|
|
32
|
-
|
|
81
|
+
variant,
|
|
82
|
+
fullWidth,
|
|
83
|
+
reportTriggerLayout
|
|
84
|
+
}), [value, onValueChange, variant, fullWidth, reportTriggerLayout]);
|
|
33
85
|
const containerStyle = (0, _react.useMemo)(() => {
|
|
34
86
|
const base = {
|
|
35
87
|
flexDirection: 'row',
|
|
@@ -54,14 +106,33 @@ const TabsBarComponent = ({
|
|
|
54
106
|
}
|
|
55
107
|
return base;
|
|
56
108
|
}, [variant, theme]);
|
|
109
|
+
const indicator = isUnderline ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
110
|
+
pointerEvents: "none",
|
|
111
|
+
testID: testID ? `${testID}-indicator` : undefined,
|
|
112
|
+
style: {
|
|
113
|
+
position: 'absolute',
|
|
114
|
+
left: 0,
|
|
115
|
+
bottom: 0,
|
|
116
|
+
height: 2,
|
|
117
|
+
backgroundColor: theme.colors.primary,
|
|
118
|
+
width: indicatorWidth,
|
|
119
|
+
transform: [{
|
|
120
|
+
translateX: indicatorX
|
|
121
|
+
}]
|
|
122
|
+
}
|
|
123
|
+
}) : null;
|
|
57
124
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(TabsContext.Provider, {
|
|
58
125
|
value: contextValue,
|
|
59
|
-
children: /*#__PURE__*/(0, _jsxRuntime.
|
|
126
|
+
children: fullWidth ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
127
|
+
style: [containerStyle, style],
|
|
128
|
+
testID: testID,
|
|
129
|
+
children: [children, indicator]
|
|
130
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
|
|
60
131
|
horizontal: true,
|
|
61
132
|
showsHorizontalScrollIndicator: false,
|
|
62
133
|
contentContainerStyle: [containerStyle, style],
|
|
63
134
|
testID: testID,
|
|
64
|
-
children: children
|
|
135
|
+
children: [children, indicator]
|
|
65
136
|
})
|
|
66
137
|
});
|
|
67
138
|
};
|
|
@@ -77,7 +148,9 @@ const TabComponent = ({
|
|
|
77
148
|
const {
|
|
78
149
|
value: selectedValue,
|
|
79
150
|
onValueChange,
|
|
80
|
-
variant
|
|
151
|
+
variant,
|
|
152
|
+
fullWidth,
|
|
153
|
+
reportTriggerLayout
|
|
81
154
|
} = (0, _react.useContext)(TabsContext);
|
|
82
155
|
const isSelected = value === selectedValue;
|
|
83
156
|
const {
|
|
@@ -101,11 +174,8 @@ const TabComponent = ({
|
|
|
101
174
|
};
|
|
102
175
|
switch (variant) {
|
|
103
176
|
case 'underline':
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
base.borderBottomColor = theme.colors.primary;
|
|
107
|
-
base.marginBottom = -1; // overlap container border
|
|
108
|
-
}
|
|
177
|
+
// Active state is drawn by the shared sliding indicator, not a
|
|
178
|
+
// per-trigger border — nothing to add here.
|
|
109
179
|
break;
|
|
110
180
|
case 'filled':
|
|
111
181
|
if (isSelected) {
|
|
@@ -145,13 +215,27 @@ const TabComponent = ({
|
|
|
145
215
|
return base;
|
|
146
216
|
}, [variant, isSelected, theme]);
|
|
147
217
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
148
|
-
|
|
218
|
+
onLayout: e => {
|
|
219
|
+
const {
|
|
220
|
+
x,
|
|
221
|
+
width
|
|
222
|
+
} = e.nativeEvent.layout;
|
|
223
|
+
reportTriggerLayout(value, {
|
|
224
|
+
x,
|
|
225
|
+
width
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
style: [{
|
|
149
229
|
transform: [{
|
|
150
230
|
scale: scaleAnim
|
|
151
231
|
}]
|
|
152
|
-
},
|
|
232
|
+
}, fullWidth && {
|
|
233
|
+
flex: 1
|
|
234
|
+
}],
|
|
153
235
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
154
|
-
style: [tabStyle,
|
|
236
|
+
style: [tabStyle, fullWidth && {
|
|
237
|
+
flex: 1
|
|
238
|
+
}, disabled && {
|
|
155
239
|
opacity: 0.4
|
|
156
240
|
}, style],
|
|
157
241
|
onPress: handlePress,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_usePressAnimation","_tokens","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","TabsContext","createContext","value","onValueChange","variant","TabsBarComponent","children","style","testID","theme","useTheme","contextValue","useMemo","containerStyle","base","flexDirection","alignItems","borderBottomWidth","borderBottomColor","colors","borderLight","backgroundColor","backgroundSecondary","borderRadius","sm","padding","borderWidth","borderColor","border","jsx","Provider","ScrollView","horizontal","showsHorizontalScrollIndicator","contentContainerStyle","TabComponent","label","icon","disabled","textStyle","selectedValue","useContext","isSelected","scaleAnim","onPressIn","onPressOut","usePressAnimation","handlePress","useCallback","tabStyle","justifyContent","paddingHorizontal","space","lg","paddingVertical","gap","xs","primary","marginBottom","card","shadowColor","shadow","shadowOffset","width","height","shadowOpacity","shadowRadius","elevation","labelStyle","fontSize","fontWeight","color","primaryForeground","textSecondary","Animated","View","transform","scale","jsxs","Pressable","opacity","onPress","accessibilityRole","accessibilityState","selected","Text","TabPanelComponent","Tabs","exports","memo","displayName","TabsTrigger","TabsContent"],"sourceRoot":"../../../src","sources":["tabs/Tabs.tsx"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_usePressAnimation","_tokens","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","TabsContext","createContext","value","onValueChange","variant","fullWidth","reportTriggerLayout","TabsBarComponent","children","style","testID","theme","useTheme","isUnderline","indicatorX","useRef","Animated","Value","current","indicatorWidth","triggerLayoutsRef","indicatorPlacedRef","moveIndicator","useCallback","target","animate","parallel","timing","toValue","x","duration","easing","Easing","out","cubic","useNativeDriver","width","start","setValue","tabValue","layout","useEffect","contextValue","useMemo","containerStyle","base","flexDirection","alignItems","borderBottomWidth","borderBottomColor","colors","borderLight","backgroundColor","backgroundSecondary","borderRadius","sm","padding","borderWidth","borderColor","border","indicator","jsx","View","pointerEvents","undefined","position","left","bottom","height","primary","transform","translateX","Provider","jsxs","ScrollView","horizontal","showsHorizontalScrollIndicator","contentContainerStyle","TabComponent","label","icon","disabled","textStyle","selectedValue","useContext","isSelected","scaleAnim","onPressIn","onPressOut","usePressAnimation","handlePress","tabStyle","justifyContent","paddingHorizontal","space","lg","paddingVertical","gap","xs","card","shadowColor","shadow","shadowOffset","shadowOpacity","shadowRadius","elevation","labelStyle","fontSize","fontWeight","color","primaryForeground","textSecondary","onLayout","nativeEvent","scale","flex","Pressable","opacity","onPress","accessibilityRole","accessibilityState","selected","Text","TabPanelComponent","Tabs","exports","memo","displayName","TabsTrigger","TabsContent"],"sourceRoot":"../../../src","sources":["tabs/Tabs.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/module/tabs/Tabs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { createContext, memo, useCallback, useContext, useMemo } from 'react';
|
|
4
|
-
import { View, Text, Pressable, Animated, ScrollView } from 'react-native';
|
|
3
|
+
import React, { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
|
|
4
|
+
import { View, Text, Pressable, Animated, Easing, ScrollView } from 'react-native';
|
|
5
5
|
import { useTheme } from "../theme/use-theme.js";
|
|
6
6
|
import { usePressAnimation } from "../hooks/usePressAnimation.js";
|
|
7
7
|
import { borderRadius, space } from "../styles/tokens.js";
|
|
@@ -9,22 +9,74 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
const TabsContext = /*#__PURE__*/createContext({
|
|
10
10
|
value: '',
|
|
11
11
|
onValueChange: () => {},
|
|
12
|
-
variant: 'underline'
|
|
12
|
+
variant: 'underline',
|
|
13
|
+
fullWidth: false,
|
|
14
|
+
reportTriggerLayout: () => {}
|
|
13
15
|
});
|
|
14
16
|
const TabsBarComponent = ({
|
|
15
17
|
value,
|
|
16
18
|
onValueChange,
|
|
17
19
|
variant = 'underline',
|
|
20
|
+
fullWidth = false,
|
|
18
21
|
children,
|
|
19
22
|
style,
|
|
20
23
|
testID
|
|
21
24
|
}) => {
|
|
22
25
|
const theme = useTheme();
|
|
26
|
+
const isUnderline = variant === 'underline';
|
|
27
|
+
|
|
28
|
+
// Shared sliding underline: one indicator that translates + resizes between
|
|
29
|
+
// triggers. Triggers report their measured {x, width}; the container drives
|
|
30
|
+
// these Animated values imperatively (no re-render on measure or slide).
|
|
31
|
+
const indicatorX = useRef(new Animated.Value(0)).current;
|
|
32
|
+
const indicatorWidth = useRef(new Animated.Value(0)).current;
|
|
33
|
+
const triggerLayoutsRef = useRef({});
|
|
34
|
+
const indicatorPlacedRef = useRef(false);
|
|
35
|
+
const moveIndicator = useCallback((target, animate) => {
|
|
36
|
+
if (animate) {
|
|
37
|
+
Animated.parallel([Animated.timing(indicatorX, {
|
|
38
|
+
toValue: target.x,
|
|
39
|
+
duration: 200,
|
|
40
|
+
easing: Easing.out(Easing.cubic),
|
|
41
|
+
useNativeDriver: false
|
|
42
|
+
}), Animated.timing(indicatorWidth, {
|
|
43
|
+
toValue: target.width,
|
|
44
|
+
duration: 200,
|
|
45
|
+
easing: Easing.out(Easing.cubic),
|
|
46
|
+
useNativeDriver: false
|
|
47
|
+
})]).start();
|
|
48
|
+
} else {
|
|
49
|
+
indicatorX.setValue(target.x);
|
|
50
|
+
indicatorWidth.setValue(target.width);
|
|
51
|
+
}
|
|
52
|
+
}, [indicatorX, indicatorWidth]);
|
|
53
|
+
const reportTriggerLayout = useCallback((tabValue, layout) => {
|
|
54
|
+
triggerLayoutsRef.current[tabValue] = layout;
|
|
55
|
+
// Snap onto the active trigger the moment it's first measured (mount) or
|
|
56
|
+
// when its size changes — never slide in from the origin.
|
|
57
|
+
if (tabValue === value) {
|
|
58
|
+
moveIndicator(layout, false);
|
|
59
|
+
indicatorPlacedRef.current = true;
|
|
60
|
+
}
|
|
61
|
+
}, [value, moveIndicator]);
|
|
62
|
+
|
|
63
|
+
// Slide the indicator to the newly-selected trigger. Syncing an imperative
|
|
64
|
+
// Animated value to the controlled `value` prop is a legitimate effect
|
|
65
|
+
// (external-system sync); the first placement snaps, later changes animate.
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!isUnderline) return;
|
|
68
|
+
const target = triggerLayoutsRef.current[value];
|
|
69
|
+
if (!target) return; // not measured yet — reportTriggerLayout will place it
|
|
70
|
+
moveIndicator(target, indicatorPlacedRef.current);
|
|
71
|
+
indicatorPlacedRef.current = true;
|
|
72
|
+
}, [value, isUnderline, moveIndicator]);
|
|
23
73
|
const contextValue = useMemo(() => ({
|
|
24
74
|
value,
|
|
25
75
|
onValueChange,
|
|
26
|
-
variant
|
|
27
|
-
|
|
76
|
+
variant,
|
|
77
|
+
fullWidth,
|
|
78
|
+
reportTriggerLayout
|
|
79
|
+
}), [value, onValueChange, variant, fullWidth, reportTriggerLayout]);
|
|
28
80
|
const containerStyle = useMemo(() => {
|
|
29
81
|
const base = {
|
|
30
82
|
flexDirection: 'row',
|
|
@@ -49,14 +101,33 @@ const TabsBarComponent = ({
|
|
|
49
101
|
}
|
|
50
102
|
return base;
|
|
51
103
|
}, [variant, theme]);
|
|
104
|
+
const indicator = isUnderline ? /*#__PURE__*/_jsx(Animated.View, {
|
|
105
|
+
pointerEvents: "none",
|
|
106
|
+
testID: testID ? `${testID}-indicator` : undefined,
|
|
107
|
+
style: {
|
|
108
|
+
position: 'absolute',
|
|
109
|
+
left: 0,
|
|
110
|
+
bottom: 0,
|
|
111
|
+
height: 2,
|
|
112
|
+
backgroundColor: theme.colors.primary,
|
|
113
|
+
width: indicatorWidth,
|
|
114
|
+
transform: [{
|
|
115
|
+
translateX: indicatorX
|
|
116
|
+
}]
|
|
117
|
+
}
|
|
118
|
+
}) : null;
|
|
52
119
|
return /*#__PURE__*/_jsx(TabsContext.Provider, {
|
|
53
120
|
value: contextValue,
|
|
54
|
-
children: /*#__PURE__*/
|
|
121
|
+
children: fullWidth ? /*#__PURE__*/_jsxs(View, {
|
|
122
|
+
style: [containerStyle, style],
|
|
123
|
+
testID: testID,
|
|
124
|
+
children: [children, indicator]
|
|
125
|
+
}) : /*#__PURE__*/_jsxs(ScrollView, {
|
|
55
126
|
horizontal: true,
|
|
56
127
|
showsHorizontalScrollIndicator: false,
|
|
57
128
|
contentContainerStyle: [containerStyle, style],
|
|
58
129
|
testID: testID,
|
|
59
|
-
children: children
|
|
130
|
+
children: [children, indicator]
|
|
60
131
|
})
|
|
61
132
|
});
|
|
62
133
|
};
|
|
@@ -72,7 +143,9 @@ const TabComponent = ({
|
|
|
72
143
|
const {
|
|
73
144
|
value: selectedValue,
|
|
74
145
|
onValueChange,
|
|
75
|
-
variant
|
|
146
|
+
variant,
|
|
147
|
+
fullWidth,
|
|
148
|
+
reportTriggerLayout
|
|
76
149
|
} = useContext(TabsContext);
|
|
77
150
|
const isSelected = value === selectedValue;
|
|
78
151
|
const {
|
|
@@ -96,11 +169,8 @@ const TabComponent = ({
|
|
|
96
169
|
};
|
|
97
170
|
switch (variant) {
|
|
98
171
|
case 'underline':
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
base.borderBottomColor = theme.colors.primary;
|
|
102
|
-
base.marginBottom = -1; // overlap container border
|
|
103
|
-
}
|
|
172
|
+
// Active state is drawn by the shared sliding indicator, not a
|
|
173
|
+
// per-trigger border — nothing to add here.
|
|
104
174
|
break;
|
|
105
175
|
case 'filled':
|
|
106
176
|
if (isSelected) {
|
|
@@ -140,13 +210,27 @@ const TabComponent = ({
|
|
|
140
210
|
return base;
|
|
141
211
|
}, [variant, isSelected, theme]);
|
|
142
212
|
return /*#__PURE__*/_jsx(Animated.View, {
|
|
143
|
-
|
|
213
|
+
onLayout: e => {
|
|
214
|
+
const {
|
|
215
|
+
x,
|
|
216
|
+
width
|
|
217
|
+
} = e.nativeEvent.layout;
|
|
218
|
+
reportTriggerLayout(value, {
|
|
219
|
+
x,
|
|
220
|
+
width
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
style: [{
|
|
144
224
|
transform: [{
|
|
145
225
|
scale: scaleAnim
|
|
146
226
|
}]
|
|
147
|
-
},
|
|
227
|
+
}, fullWidth && {
|
|
228
|
+
flex: 1
|
|
229
|
+
}],
|
|
148
230
|
children: /*#__PURE__*/_jsxs(Pressable, {
|
|
149
|
-
style: [tabStyle,
|
|
231
|
+
style: [tabStyle, fullWidth && {
|
|
232
|
+
flex: 1
|
|
233
|
+
}, disabled && {
|
|
150
234
|
opacity: 0.4
|
|
151
235
|
}, style],
|
|
152
236
|
onPress: handlePress,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","createContext","memo","useCallback","useContext","useMemo","View","Text","Pressable","Animated","ScrollView","useTheme","usePressAnimation","borderRadius","space","jsx","_jsx","jsxs","_jsxs","TabsContext","value","onValueChange","variant","TabsBarComponent","children","style","testID","theme","contextValue","containerStyle","base","flexDirection","alignItems","borderBottomWidth","borderBottomColor","colors","borderLight","backgroundColor","backgroundSecondary","sm","padding","borderWidth","borderColor","border","Provider","horizontal","showsHorizontalScrollIndicator","contentContainerStyle","TabComponent","label","icon","disabled","textStyle","selectedValue","isSelected","scaleAnim","onPressIn","onPressOut","handlePress","tabStyle","justifyContent","paddingHorizontal","lg","paddingVertical","gap","xs","
|
|
1
|
+
{"version":3,"names":["React","createContext","memo","useCallback","useContext","useEffect","useMemo","useRef","View","Text","Pressable","Animated","Easing","ScrollView","useTheme","usePressAnimation","borderRadius","space","jsx","_jsx","jsxs","_jsxs","TabsContext","value","onValueChange","variant","fullWidth","reportTriggerLayout","TabsBarComponent","children","style","testID","theme","isUnderline","indicatorX","Value","current","indicatorWidth","triggerLayoutsRef","indicatorPlacedRef","moveIndicator","target","animate","parallel","timing","toValue","x","duration","easing","out","cubic","useNativeDriver","width","start","setValue","tabValue","layout","contextValue","containerStyle","base","flexDirection","alignItems","borderBottomWidth","borderBottomColor","colors","borderLight","backgroundColor","backgroundSecondary","sm","padding","borderWidth","borderColor","border","indicator","pointerEvents","undefined","position","left","bottom","height","primary","transform","translateX","Provider","horizontal","showsHorizontalScrollIndicator","contentContainerStyle","TabComponent","label","icon","disabled","textStyle","selectedValue","isSelected","scaleAnim","onPressIn","onPressOut","handlePress","tabStyle","justifyContent","paddingHorizontal","lg","paddingVertical","gap","xs","card","shadowColor","shadow","shadowOffset","shadowOpacity","shadowRadius","elevation","labelStyle","fontSize","fontWeight","color","primaryForeground","textSecondary","onLayout","e","nativeEvent","scale","flex","opacity","onPress","accessibilityRole","accessibilityState","selected","TabPanelComponent","Tabs","displayName","TabsTrigger","TabsContent"],"sourceRoot":"../../../src","sources":["tabs/Tabs.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAgBf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA+Q1F,eAAO,MAAM,IAAI,uCAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
|
|
@@ -7,6 +7,13 @@ export interface TabsProps {
|
|
|
7
7
|
onValueChange: (value: string) => void;
|
|
8
8
|
/** Visual variant for the tab bar. */
|
|
9
9
|
variant?: TabsVariant;
|
|
10
|
+
/**
|
|
11
|
+
* When true, the tab bar spans the full container width and each trigger
|
|
12
|
+
* takes an equal share of it (edge to edge), instead of the default
|
|
13
|
+
* content-sized, horizontally scrollable layout. Best for a small, fixed
|
|
14
|
+
* set of tabs (e.g. 2–3). Defaults to `false`.
|
|
15
|
+
*/
|
|
16
|
+
fullWidth?: boolean;
|
|
10
17
|
/** The tab items. Must be TabsTrigger components. */
|
|
11
18
|
children: React.ReactNode;
|
|
12
19
|
style?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../src/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAgBf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA+Q1F,eAAO,MAAM,IAAI,uCAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
|
|
@@ -7,6 +7,13 @@ export interface TabsProps {
|
|
|
7
7
|
onValueChange: (value: string) => void;
|
|
8
8
|
/** Visual variant for the tab bar. */
|
|
9
9
|
variant?: TabsVariant;
|
|
10
|
+
/**
|
|
11
|
+
* When true, the tab bar spans the full container width and each trigger
|
|
12
|
+
* takes an equal share of it (edge to edge), instead of the default
|
|
13
|
+
* content-sized, horizontally scrollable layout. Best for a small, fixed
|
|
14
|
+
* set of tabs (e.g. 2–3). Defaults to `false`.
|
|
15
|
+
*/
|
|
16
|
+
fullWidth?: boolean;
|
|
10
17
|
/** The tab items. Must be TabsTrigger components. */
|
|
11
18
|
children: React.ReactNode;
|
|
12
19
|
style?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/tabs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sCAAsC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReactTestInstance } from 'react-test-renderer';
|
|
3
|
+
import { fireEvent, render } from '@testing-library/react-native';
|
|
4
|
+
|
|
5
|
+
import { BloomThemeProvider } from '../theme/BloomThemeProvider';
|
|
6
|
+
import { Tabs, TabsTrigger } from '../tabs';
|
|
7
|
+
|
|
8
|
+
function renderWithTheme(ui: React.ReactElement) {
|
|
9
|
+
return render(
|
|
10
|
+
<BloomThemeProvider mode="light" colorPreset="teal">
|
|
11
|
+
{ui}
|
|
12
|
+
</BloomThemeProvider>,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Flatten a (possibly nested / conditional) RN style prop into one object.
|
|
17
|
+
function flattenStyle(style: unknown): Record<string, unknown> {
|
|
18
|
+
const out: Record<string, unknown> = {};
|
|
19
|
+
const visit = (s: unknown): void => {
|
|
20
|
+
if (Array.isArray(s)) s.forEach(visit);
|
|
21
|
+
else if (s && typeof s === 'object') Object.assign(out, s);
|
|
22
|
+
};
|
|
23
|
+
visit(style);
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Walk up from a label's Text node to the trigger Pressable (role="tab").
|
|
28
|
+
function triggerFor(node: ReactTestInstance): ReactTestInstance {
|
|
29
|
+
let current: ReactTestInstance | null = node;
|
|
30
|
+
while (current && current.props?.accessibilityRole !== 'tab') {
|
|
31
|
+
current = current.parent;
|
|
32
|
+
}
|
|
33
|
+
if (!current) throw new Error('No trigger ancestor with role "tab" found');
|
|
34
|
+
return current;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Bar({
|
|
38
|
+
value,
|
|
39
|
+
onValueChange = () => {},
|
|
40
|
+
...props
|
|
41
|
+
}: {
|
|
42
|
+
value: string;
|
|
43
|
+
onValueChange?: (v: string) => void;
|
|
44
|
+
} & Partial<React.ComponentProps<typeof Tabs>>) {
|
|
45
|
+
return (
|
|
46
|
+
<Tabs value={value} onValueChange={onValueChange} testID="tabs" {...props}>
|
|
47
|
+
<TabsTrigger value="a" label="First" />
|
|
48
|
+
<TabsTrigger value="b" label="Second" />
|
|
49
|
+
</Tabs>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe('Tabs', () => {
|
|
54
|
+
it('renders trigger labels', () => {
|
|
55
|
+
const { getByText } = renderWithTheme(<Bar value="a" />);
|
|
56
|
+
expect(getByText('First')).toBeTruthy();
|
|
57
|
+
expect(getByText('Second')).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('calls onValueChange when a trigger is pressed', () => {
|
|
61
|
+
const onValueChange = jest.fn();
|
|
62
|
+
const { getByText } = renderWithTheme(
|
|
63
|
+
<Bar value="a" onValueChange={onValueChange} />,
|
|
64
|
+
);
|
|
65
|
+
fireEvent.press(getByText('Second'));
|
|
66
|
+
expect(onValueChange).toHaveBeenCalledWith('b');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('renders exactly one shared underline indicator, not per-trigger borders', () => {
|
|
70
|
+
const { getByTestId, getByText } = renderWithTheme(<Bar value="a" />);
|
|
71
|
+
// One shared indicator element...
|
|
72
|
+
expect(getByTestId('tabs-indicator')).toBeTruthy();
|
|
73
|
+
// ...and no trigger draws the old static active bottom border.
|
|
74
|
+
for (const label of ['First', 'Second']) {
|
|
75
|
+
const trigger = triggerFor(getByText(label));
|
|
76
|
+
expect(flattenStyle(trigger.props.style).borderBottomWidth).toBeUndefined();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('does not render the underline indicator for the filled variant', () => {
|
|
81
|
+
const { queryByTestId } = renderWithTheme(<Bar value="a" variant="filled" />);
|
|
82
|
+
expect(queryByTestId('tabs-indicator')).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('stretches each trigger to equal width when fullWidth is set', () => {
|
|
86
|
+
const { getByText } = renderWithTheme(<Bar value="a" fullWidth />);
|
|
87
|
+
for (const label of ['First', 'Second']) {
|
|
88
|
+
expect(flattenStyle(triggerFor(getByText(label)).props.style).flex).toBe(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('keeps triggers content-sized (no flex) by default', () => {
|
|
93
|
+
const { getByText } = renderWithTheme(<Bar value="a" />);
|
|
94
|
+
for (const label of ['First', 'Second']) {
|
|
95
|
+
expect(
|
|
96
|
+
flattenStyle(triggerFor(getByText(label)).props.style).flex,
|
|
97
|
+
).toBeUndefined();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('marks the active trigger as selected for accessibility', () => {
|
|
102
|
+
const { getByText } = renderWithTheme(<Bar value="b" />);
|
|
103
|
+
expect(
|
|
104
|
+
triggerFor(getByText('First')).props.accessibilityState.selected,
|
|
105
|
+
).toBe(false);
|
|
106
|
+
expect(
|
|
107
|
+
triggerFor(getByText('Second')).props.accessibilityState.selected,
|
|
108
|
+
).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
package/src/tabs/Tabs.tsx
CHANGED
|
@@ -3,14 +3,18 @@ import React, {
|
|
|
3
3
|
memo,
|
|
4
4
|
useCallback,
|
|
5
5
|
useContext,
|
|
6
|
+
useEffect,
|
|
6
7
|
useMemo,
|
|
8
|
+
useRef,
|
|
7
9
|
} from 'react';
|
|
8
10
|
import {
|
|
9
11
|
View,
|
|
10
12
|
Text,
|
|
11
13
|
Pressable,
|
|
12
14
|
Animated,
|
|
15
|
+
Easing,
|
|
13
16
|
ScrollView,
|
|
17
|
+
type LayoutRectangle,
|
|
14
18
|
type ViewStyle,
|
|
15
19
|
type TextStyle,
|
|
16
20
|
} from 'react-native';
|
|
@@ -20,31 +24,97 @@ import { usePressAnimation } from '../hooks/usePressAnimation';
|
|
|
20
24
|
import { borderRadius, space } from '../styles/tokens';
|
|
21
25
|
import type { TabsProps, TabsTriggerProps, TabsContentProps, TabsVariant } from './types';
|
|
22
26
|
|
|
27
|
+
type TriggerLayout = Pick<LayoutRectangle, 'x' | 'width'>;
|
|
28
|
+
|
|
23
29
|
interface TabsContextValue {
|
|
24
30
|
value: string;
|
|
25
31
|
onValueChange: (value: string) => void;
|
|
26
32
|
variant: TabsVariant;
|
|
33
|
+
fullWidth: boolean;
|
|
34
|
+
/** A trigger reports its measured position so the shared underline can track it. */
|
|
35
|
+
reportTriggerLayout: (value: string, layout: TriggerLayout) => void;
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
const TabsContext = createContext<TabsContextValue>({
|
|
30
39
|
value: '',
|
|
31
40
|
onValueChange: () => {},
|
|
32
41
|
variant: 'underline',
|
|
42
|
+
fullWidth: false,
|
|
43
|
+
reportTriggerLayout: () => {},
|
|
33
44
|
});
|
|
34
45
|
|
|
35
46
|
const TabsBarComponent: React.FC<TabsProps> = ({
|
|
36
47
|
value,
|
|
37
48
|
onValueChange,
|
|
38
49
|
variant = 'underline',
|
|
50
|
+
fullWidth = false,
|
|
39
51
|
children,
|
|
40
52
|
style,
|
|
41
53
|
testID,
|
|
42
54
|
}) => {
|
|
43
55
|
const theme = useTheme();
|
|
56
|
+
const isUnderline = variant === 'underline';
|
|
57
|
+
|
|
58
|
+
// Shared sliding underline: one indicator that translates + resizes between
|
|
59
|
+
// triggers. Triggers report their measured {x, width}; the container drives
|
|
60
|
+
// these Animated values imperatively (no re-render on measure or slide).
|
|
61
|
+
const indicatorX = useRef(new Animated.Value(0)).current;
|
|
62
|
+
const indicatorWidth = useRef(new Animated.Value(0)).current;
|
|
63
|
+
const triggerLayoutsRef = useRef<Record<string, TriggerLayout>>({});
|
|
64
|
+
const indicatorPlacedRef = useRef(false);
|
|
65
|
+
|
|
66
|
+
const moveIndicator = useCallback(
|
|
67
|
+
(target: TriggerLayout, animate: boolean) => {
|
|
68
|
+
if (animate) {
|
|
69
|
+
Animated.parallel([
|
|
70
|
+
Animated.timing(indicatorX, {
|
|
71
|
+
toValue: target.x,
|
|
72
|
+
duration: 200,
|
|
73
|
+
easing: Easing.out(Easing.cubic),
|
|
74
|
+
useNativeDriver: false,
|
|
75
|
+
}),
|
|
76
|
+
Animated.timing(indicatorWidth, {
|
|
77
|
+
toValue: target.width,
|
|
78
|
+
duration: 200,
|
|
79
|
+
easing: Easing.out(Easing.cubic),
|
|
80
|
+
useNativeDriver: false,
|
|
81
|
+
}),
|
|
82
|
+
]).start();
|
|
83
|
+
} else {
|
|
84
|
+
indicatorX.setValue(target.x);
|
|
85
|
+
indicatorWidth.setValue(target.width);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
[indicatorX, indicatorWidth],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const reportTriggerLayout = useCallback(
|
|
92
|
+
(tabValue: string, layout: TriggerLayout) => {
|
|
93
|
+
triggerLayoutsRef.current[tabValue] = layout;
|
|
94
|
+
// Snap onto the active trigger the moment it's first measured (mount) or
|
|
95
|
+
// when its size changes — never slide in from the origin.
|
|
96
|
+
if (tabValue === value) {
|
|
97
|
+
moveIndicator(layout, false);
|
|
98
|
+
indicatorPlacedRef.current = true;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
[value, moveIndicator],
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Slide the indicator to the newly-selected trigger. Syncing an imperative
|
|
105
|
+
// Animated value to the controlled `value` prop is a legitimate effect
|
|
106
|
+
// (external-system sync); the first placement snaps, later changes animate.
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!isUnderline) return;
|
|
109
|
+
const target = triggerLayoutsRef.current[value];
|
|
110
|
+
if (!target) return; // not measured yet — reportTriggerLayout will place it
|
|
111
|
+
moveIndicator(target, indicatorPlacedRef.current);
|
|
112
|
+
indicatorPlacedRef.current = true;
|
|
113
|
+
}, [value, isUnderline, moveIndicator]);
|
|
44
114
|
|
|
45
115
|
const contextValue = useMemo(
|
|
46
|
-
() => ({ value, onValueChange, variant }),
|
|
47
|
-
[value, onValueChange, variant],
|
|
116
|
+
() => ({ value, onValueChange, variant, fullWidth, reportTriggerLayout }),
|
|
117
|
+
[value, onValueChange, variant, fullWidth, reportTriggerLayout],
|
|
48
118
|
);
|
|
49
119
|
|
|
50
120
|
const containerStyle = useMemo((): ViewStyle => {
|
|
@@ -74,16 +144,40 @@ const TabsBarComponent: React.FC<TabsProps> = ({
|
|
|
74
144
|
return base;
|
|
75
145
|
}, [variant, theme]);
|
|
76
146
|
|
|
147
|
+
const indicator = isUnderline ? (
|
|
148
|
+
<Animated.View
|
|
149
|
+
pointerEvents="none"
|
|
150
|
+
testID={testID ? `${testID}-indicator` : undefined}
|
|
151
|
+
style={{
|
|
152
|
+
position: 'absolute',
|
|
153
|
+
left: 0,
|
|
154
|
+
bottom: 0,
|
|
155
|
+
height: 2,
|
|
156
|
+
backgroundColor: theme.colors.primary,
|
|
157
|
+
width: indicatorWidth,
|
|
158
|
+
transform: [{ translateX: indicatorX }],
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
161
|
+
) : null;
|
|
162
|
+
|
|
77
163
|
return (
|
|
78
164
|
<TabsContext.Provider value={contextValue}>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
165
|
+
{fullWidth ? (
|
|
166
|
+
<View style={[containerStyle, style]} testID={testID}>
|
|
167
|
+
{children}
|
|
168
|
+
{indicator}
|
|
169
|
+
</View>
|
|
170
|
+
) : (
|
|
171
|
+
<ScrollView
|
|
172
|
+
horizontal
|
|
173
|
+
showsHorizontalScrollIndicator={false}
|
|
174
|
+
contentContainerStyle={[containerStyle, style]}
|
|
175
|
+
testID={testID}
|
|
176
|
+
>
|
|
177
|
+
{children}
|
|
178
|
+
{indicator}
|
|
179
|
+
</ScrollView>
|
|
180
|
+
)}
|
|
87
181
|
</TabsContext.Provider>
|
|
88
182
|
);
|
|
89
183
|
};
|
|
@@ -97,7 +191,13 @@ const TabComponent: React.FC<TabsTriggerProps> = ({
|
|
|
97
191
|
textStyle,
|
|
98
192
|
}) => {
|
|
99
193
|
const theme = useTheme();
|
|
100
|
-
const {
|
|
194
|
+
const {
|
|
195
|
+
value: selectedValue,
|
|
196
|
+
onValueChange,
|
|
197
|
+
variant,
|
|
198
|
+
fullWidth,
|
|
199
|
+
reportTriggerLayout,
|
|
200
|
+
} = useContext(TabsContext);
|
|
101
201
|
const isSelected = value === selectedValue;
|
|
102
202
|
const { scaleAnim, onPressIn, onPressOut } = usePressAnimation(0.97);
|
|
103
203
|
|
|
@@ -119,11 +219,8 @@ const TabComponent: React.FC<TabsTriggerProps> = ({
|
|
|
119
219
|
|
|
120
220
|
switch (variant) {
|
|
121
221
|
case 'underline':
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
base.borderBottomColor = theme.colors.primary;
|
|
125
|
-
base.marginBottom = -1; // overlap container border
|
|
126
|
-
}
|
|
222
|
+
// Active state is drawn by the shared sliding indicator, not a
|
|
223
|
+
// per-trigger border — nothing to add here.
|
|
127
224
|
break;
|
|
128
225
|
case 'filled':
|
|
129
226
|
if (isSelected) {
|
|
@@ -165,9 +262,15 @@ const TabComponent: React.FC<TabsTriggerProps> = ({
|
|
|
165
262
|
}, [variant, isSelected, theme]);
|
|
166
263
|
|
|
167
264
|
return (
|
|
168
|
-
<Animated.View
|
|
265
|
+
<Animated.View
|
|
266
|
+
onLayout={(e) => {
|
|
267
|
+
const { x, width } = e.nativeEvent.layout;
|
|
268
|
+
reportTriggerLayout(value, { x, width });
|
|
269
|
+
}}
|
|
270
|
+
style={[{ transform: [{ scale: scaleAnim }] }, fullWidth && { flex: 1 }]}
|
|
271
|
+
>
|
|
169
272
|
<Pressable
|
|
170
|
-
style={[tabStyle, disabled && { opacity: 0.4 }, style]}
|
|
273
|
+
style={[tabStyle, fullWidth && { flex: 1 }, disabled && { opacity: 0.4 }, style]}
|
|
171
274
|
onPress={handlePress}
|
|
172
275
|
onPressIn={onPressIn}
|
|
173
276
|
onPressOut={onPressOut}
|
package/src/tabs/types.ts
CHANGED
|
@@ -9,6 +9,13 @@ export interface TabsProps {
|
|
|
9
9
|
onValueChange: (value: string) => void;
|
|
10
10
|
/** Visual variant for the tab bar. */
|
|
11
11
|
variant?: TabsVariant;
|
|
12
|
+
/**
|
|
13
|
+
* When true, the tab bar spans the full container width and each trigger
|
|
14
|
+
* takes an equal share of it (edge to edge), instead of the default
|
|
15
|
+
* content-sized, horizontally scrollable layout. Best for a small, fixed
|
|
16
|
+
* set of tabs (e.g. 2–3). Defaults to `false`.
|
|
17
|
+
*/
|
|
18
|
+
fullWidth?: boolean;
|
|
12
19
|
/** The tab items. Must be TabsTrigger components. */
|
|
13
20
|
children: React.ReactNode;
|
|
14
21
|
style?: StyleProp<ViewStyle>;
|