@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.
@@ -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
- }), [value, onValueChange, variant]);
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.jsx)(_reactNative.ScrollView, {
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
- if (isSelected) {
105
- base.borderBottomWidth = 2;
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
- style: {
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, disabled && {
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":[]}
@@ -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
- }), [value, onValueChange, variant]);
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__*/_jsx(ScrollView, {
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
- if (isSelected) {
100
- base.borderBottomWidth = 2;
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
- style: {
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, disabled && {
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","primary","marginBottom","card","shadowColor","shadow","shadowOffset","width","height","shadowOpacity","shadowRadius","elevation","labelStyle","fontSize","fontWeight","color","primaryForeground","textSecondary","transform","scale","opacity","onPress","accessibilityRole","accessibilityState","selected","TabPanelComponent","Tabs","displayName","TabsTrigger","TabsContent"],"sourceRoot":"../../../src","sources":["tabs/Tabs.tsx"],"mappings":"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","ignoreList":[]}
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,KAMN,MAAM,OAAO,CAAC;AAcf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA4K1F,eAAO,MAAM,IAAI,uCAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
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,KAMN,MAAM,OAAO,CAAC;AAcf,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AA4K1F,eAAO,MAAM,IAAI,uCAAyB,CAAC;AAG3C,eAAO,MAAM,WAAW,8CAAqB,CAAC;AAG9C,eAAO,MAAM,WAAW,8CAA0B,CAAC"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.35.2",
3
+ "version": "0.35.4",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -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
- <ScrollView
80
- horizontal
81
- showsHorizontalScrollIndicator={false}
82
- contentContainerStyle={[containerStyle, style]}
83
- testID={testID}
84
- >
85
- {children}
86
- </ScrollView>
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 { value: selectedValue, onValueChange, variant } = useContext(TabsContext);
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
- if (isSelected) {
123
- base.borderBottomWidth = 2;
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 style={{ transform: [{ scale: scaleAnim }] }}>
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>;