@oxyhq/bloom 0.35.3 → 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 +85 -13
- package/lib/commonjs/tabs/Tabs.js.map +1 -1
- package/lib/module/tabs/Tabs.js +87 -15
- package/lib/module/tabs/Tabs.js.map +1 -1
- package/lib/typescript/commonjs/tabs/Tabs.d.ts.map +1 -1
- package/lib/typescript/module/tabs/Tabs.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/Tabs.test.tsx +110 -0
- package/src/tabs/Tabs.tsx +94 -7
|
@@ -15,7 +15,8 @@ const TabsContext = /*#__PURE__*/(0, _react.createContext)({
|
|
|
15
15
|
value: '',
|
|
16
16
|
onValueChange: () => {},
|
|
17
17
|
variant: 'underline',
|
|
18
|
-
fullWidth: false
|
|
18
|
+
fullWidth: false,
|
|
19
|
+
reportTriggerLayout: () => {}
|
|
19
20
|
});
|
|
20
21
|
const TabsBarComponent = ({
|
|
21
22
|
value,
|
|
@@ -27,12 +28,60 @@ const TabsBarComponent = ({
|
|
|
27
28
|
testID
|
|
28
29
|
}) => {
|
|
29
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]);
|
|
30
78
|
const contextValue = (0, _react.useMemo)(() => ({
|
|
31
79
|
value,
|
|
32
80
|
onValueChange,
|
|
33
81
|
variant,
|
|
34
|
-
fullWidth
|
|
35
|
-
|
|
82
|
+
fullWidth,
|
|
83
|
+
reportTriggerLayout
|
|
84
|
+
}), [value, onValueChange, variant, fullWidth, reportTriggerLayout]);
|
|
36
85
|
const containerStyle = (0, _react.useMemo)(() => {
|
|
37
86
|
const base = {
|
|
38
87
|
flexDirection: 'row',
|
|
@@ -57,18 +106,33 @@ const TabsBarComponent = ({
|
|
|
57
106
|
}
|
|
58
107
|
return base;
|
|
59
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;
|
|
60
124
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(TabsContext.Provider, {
|
|
61
125
|
value: contextValue,
|
|
62
|
-
children: fullWidth ? /*#__PURE__*/(0, _jsxRuntime.
|
|
126
|
+
children: fullWidth ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
63
127
|
style: [containerStyle, style],
|
|
64
128
|
testID: testID,
|
|
65
|
-
children: children
|
|
66
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.
|
|
129
|
+
children: [children, indicator]
|
|
130
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
|
|
67
131
|
horizontal: true,
|
|
68
132
|
showsHorizontalScrollIndicator: false,
|
|
69
133
|
contentContainerStyle: [containerStyle, style],
|
|
70
134
|
testID: testID,
|
|
71
|
-
children: children
|
|
135
|
+
children: [children, indicator]
|
|
72
136
|
})
|
|
73
137
|
});
|
|
74
138
|
};
|
|
@@ -85,7 +149,8 @@ const TabComponent = ({
|
|
|
85
149
|
value: selectedValue,
|
|
86
150
|
onValueChange,
|
|
87
151
|
variant,
|
|
88
|
-
fullWidth
|
|
152
|
+
fullWidth,
|
|
153
|
+
reportTriggerLayout
|
|
89
154
|
} = (0, _react.useContext)(TabsContext);
|
|
90
155
|
const isSelected = value === selectedValue;
|
|
91
156
|
const {
|
|
@@ -109,11 +174,8 @@ const TabComponent = ({
|
|
|
109
174
|
};
|
|
110
175
|
switch (variant) {
|
|
111
176
|
case 'underline':
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
base.borderBottomColor = theme.colors.primary;
|
|
115
|
-
base.marginBottom = -1; // overlap container border
|
|
116
|
-
}
|
|
177
|
+
// Active state is drawn by the shared sliding indicator, not a
|
|
178
|
+
// per-trigger border — nothing to add here.
|
|
117
179
|
break;
|
|
118
180
|
case 'filled':
|
|
119
181
|
if (isSelected) {
|
|
@@ -153,6 +215,16 @@ const TabComponent = ({
|
|
|
153
215
|
return base;
|
|
154
216
|
}, [variant, isSelected, theme]);
|
|
155
217
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
218
|
+
onLayout: e => {
|
|
219
|
+
const {
|
|
220
|
+
x,
|
|
221
|
+
width
|
|
222
|
+
} = e.nativeEvent.layout;
|
|
223
|
+
reportTriggerLayout(value, {
|
|
224
|
+
x,
|
|
225
|
+
width
|
|
226
|
+
});
|
|
227
|
+
},
|
|
156
228
|
style: [{
|
|
157
229
|
transform: [{
|
|
158
230
|
scale: scaleAnim
|
|
@@ -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","fullWidth","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","View","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","transform","scale","flex","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";
|
|
@@ -10,7 +10,8 @@ const TabsContext = /*#__PURE__*/createContext({
|
|
|
10
10
|
value: '',
|
|
11
11
|
onValueChange: () => {},
|
|
12
12
|
variant: 'underline',
|
|
13
|
-
fullWidth: false
|
|
13
|
+
fullWidth: false,
|
|
14
|
+
reportTriggerLayout: () => {}
|
|
14
15
|
});
|
|
15
16
|
const TabsBarComponent = ({
|
|
16
17
|
value,
|
|
@@ -22,12 +23,60 @@ const TabsBarComponent = ({
|
|
|
22
23
|
testID
|
|
23
24
|
}) => {
|
|
24
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]);
|
|
25
73
|
const contextValue = useMemo(() => ({
|
|
26
74
|
value,
|
|
27
75
|
onValueChange,
|
|
28
76
|
variant,
|
|
29
|
-
fullWidth
|
|
30
|
-
|
|
77
|
+
fullWidth,
|
|
78
|
+
reportTriggerLayout
|
|
79
|
+
}), [value, onValueChange, variant, fullWidth, reportTriggerLayout]);
|
|
31
80
|
const containerStyle = useMemo(() => {
|
|
32
81
|
const base = {
|
|
33
82
|
flexDirection: 'row',
|
|
@@ -52,18 +101,33 @@ const TabsBarComponent = ({
|
|
|
52
101
|
}
|
|
53
102
|
return base;
|
|
54
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;
|
|
55
119
|
return /*#__PURE__*/_jsx(TabsContext.Provider, {
|
|
56
120
|
value: contextValue,
|
|
57
|
-
children: fullWidth ? /*#__PURE__*/
|
|
121
|
+
children: fullWidth ? /*#__PURE__*/_jsxs(View, {
|
|
58
122
|
style: [containerStyle, style],
|
|
59
123
|
testID: testID,
|
|
60
|
-
children: children
|
|
61
|
-
}) : /*#__PURE__*/
|
|
124
|
+
children: [children, indicator]
|
|
125
|
+
}) : /*#__PURE__*/_jsxs(ScrollView, {
|
|
62
126
|
horizontal: true,
|
|
63
127
|
showsHorizontalScrollIndicator: false,
|
|
64
128
|
contentContainerStyle: [containerStyle, style],
|
|
65
129
|
testID: testID,
|
|
66
|
-
children: children
|
|
130
|
+
children: [children, indicator]
|
|
67
131
|
})
|
|
68
132
|
});
|
|
69
133
|
};
|
|
@@ -80,7 +144,8 @@ const TabComponent = ({
|
|
|
80
144
|
value: selectedValue,
|
|
81
145
|
onValueChange,
|
|
82
146
|
variant,
|
|
83
|
-
fullWidth
|
|
147
|
+
fullWidth,
|
|
148
|
+
reportTriggerLayout
|
|
84
149
|
} = useContext(TabsContext);
|
|
85
150
|
const isSelected = value === selectedValue;
|
|
86
151
|
const {
|
|
@@ -104,11 +169,8 @@ const TabComponent = ({
|
|
|
104
169
|
};
|
|
105
170
|
switch (variant) {
|
|
106
171
|
case 'underline':
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
base.borderBottomColor = theme.colors.primary;
|
|
110
|
-
base.marginBottom = -1; // overlap container border
|
|
111
|
-
}
|
|
172
|
+
// Active state is drawn by the shared sliding indicator, not a
|
|
173
|
+
// per-trigger border — nothing to add here.
|
|
112
174
|
break;
|
|
113
175
|
case 'filled':
|
|
114
176
|
if (isSelected) {
|
|
@@ -148,6 +210,16 @@ const TabComponent = ({
|
|
|
148
210
|
return base;
|
|
149
211
|
}, [variant, isSelected, theme]);
|
|
150
212
|
return /*#__PURE__*/_jsx(Animated.View, {
|
|
213
|
+
onLayout: e => {
|
|
214
|
+
const {
|
|
215
|
+
x,
|
|
216
|
+
width
|
|
217
|
+
} = e.nativeEvent.layout;
|
|
218
|
+
reportTriggerLayout(value, {
|
|
219
|
+
x,
|
|
220
|
+
width
|
|
221
|
+
});
|
|
222
|
+
},
|
|
151
223
|
style: [{
|
|
152
224
|
transform: [{
|
|
153
225
|
scale: scaleAnim
|
|
@@ -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","fullWidth","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"}
|
|
@@ -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"}
|
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,11 +24,15 @@ 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;
|
|
27
33
|
fullWidth: boolean;
|
|
34
|
+
/** A trigger reports its measured position so the shared underline can track it. */
|
|
35
|
+
reportTriggerLayout: (value: string, layout: TriggerLayout) => void;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
const TabsContext = createContext<TabsContextValue>({
|
|
@@ -32,6 +40,7 @@ const TabsContext = createContext<TabsContextValue>({
|
|
|
32
40
|
onValueChange: () => {},
|
|
33
41
|
variant: 'underline',
|
|
34
42
|
fullWidth: false,
|
|
43
|
+
reportTriggerLayout: () => {},
|
|
35
44
|
});
|
|
36
45
|
|
|
37
46
|
const TabsBarComponent: React.FC<TabsProps> = ({
|
|
@@ -44,10 +53,68 @@ const TabsBarComponent: React.FC<TabsProps> = ({
|
|
|
44
53
|
testID,
|
|
45
54
|
}) => {
|
|
46
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]);
|
|
47
114
|
|
|
48
115
|
const contextValue = useMemo(
|
|
49
|
-
() => ({ value, onValueChange, variant, fullWidth }),
|
|
50
|
-
[value, onValueChange, variant, fullWidth],
|
|
116
|
+
() => ({ value, onValueChange, variant, fullWidth, reportTriggerLayout }),
|
|
117
|
+
[value, onValueChange, variant, fullWidth, reportTriggerLayout],
|
|
51
118
|
);
|
|
52
119
|
|
|
53
120
|
const containerStyle = useMemo((): ViewStyle => {
|
|
@@ -77,11 +144,28 @@ const TabsBarComponent: React.FC<TabsProps> = ({
|
|
|
77
144
|
return base;
|
|
78
145
|
}, [variant, theme]);
|
|
79
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
|
+
|
|
80
163
|
return (
|
|
81
164
|
<TabsContext.Provider value={contextValue}>
|
|
82
165
|
{fullWidth ? (
|
|
83
166
|
<View style={[containerStyle, style]} testID={testID}>
|
|
84
167
|
{children}
|
|
168
|
+
{indicator}
|
|
85
169
|
</View>
|
|
86
170
|
) : (
|
|
87
171
|
<ScrollView
|
|
@@ -91,6 +175,7 @@ const TabsBarComponent: React.FC<TabsProps> = ({
|
|
|
91
175
|
testID={testID}
|
|
92
176
|
>
|
|
93
177
|
{children}
|
|
178
|
+
{indicator}
|
|
94
179
|
</ScrollView>
|
|
95
180
|
)}
|
|
96
181
|
</TabsContext.Provider>
|
|
@@ -111,6 +196,7 @@ const TabComponent: React.FC<TabsTriggerProps> = ({
|
|
|
111
196
|
onValueChange,
|
|
112
197
|
variant,
|
|
113
198
|
fullWidth,
|
|
199
|
+
reportTriggerLayout,
|
|
114
200
|
} = useContext(TabsContext);
|
|
115
201
|
const isSelected = value === selectedValue;
|
|
116
202
|
const { scaleAnim, onPressIn, onPressOut } = usePressAnimation(0.97);
|
|
@@ -133,11 +219,8 @@ const TabComponent: React.FC<TabsTriggerProps> = ({
|
|
|
133
219
|
|
|
134
220
|
switch (variant) {
|
|
135
221
|
case 'underline':
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
base.borderBottomColor = theme.colors.primary;
|
|
139
|
-
base.marginBottom = -1; // overlap container border
|
|
140
|
-
}
|
|
222
|
+
// Active state is drawn by the shared sliding indicator, not a
|
|
223
|
+
// per-trigger border — nothing to add here.
|
|
141
224
|
break;
|
|
142
225
|
case 'filled':
|
|
143
226
|
if (isSelected) {
|
|
@@ -180,6 +263,10 @@ const TabComponent: React.FC<TabsTriggerProps> = ({
|
|
|
180
263
|
|
|
181
264
|
return (
|
|
182
265
|
<Animated.View
|
|
266
|
+
onLayout={(e) => {
|
|
267
|
+
const { x, width } = e.nativeEvent.layout;
|
|
268
|
+
reportTriggerLayout(value, { x, width });
|
|
269
|
+
}}
|
|
183
270
|
style={[{ transform: [{ scale: scaleAnim }] }, fullWidth && { flex: 1 }]}
|
|
184
271
|
>
|
|
185
272
|
<Pressable
|