@idealyst/components 1.3.2 → 1.3.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/package.json +4 -4
- package/src/Breadcrumb/Breadcrumb.native.tsx +17 -33
- package/src/Breadcrumb/Breadcrumb.styles.tsx +76 -49
- package/src/Breadcrumb/Breadcrumb.web.tsx +60 -56
- package/src/Button/Button.styles.tsx +8 -0
- package/src/IconButton/IconButton.styles.tsx +8 -0
- package/src/IconButton/docs.ts +11 -0
- package/src/TabBar/TabBar.native.tsx +91 -33
- package/src/TabBar/TabBar.styles.tsx +175 -160
- package/src/TabBar/TabBar.web.tsx +24 -16
- package/src/examples/IconButtonExamples.tsx +177 -0
- package/src/examples/index.ts +1 -0
|
@@ -34,6 +34,77 @@ function renderIcon(
|
|
|
34
34
|
return icon;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Individual tab component to isolate useVariants calls per tab.
|
|
39
|
+
*/
|
|
40
|
+
const TabItem = ({
|
|
41
|
+
item,
|
|
42
|
+
isActive,
|
|
43
|
+
size,
|
|
44
|
+
type,
|
|
45
|
+
iconPosition,
|
|
46
|
+
justify,
|
|
47
|
+
onPress,
|
|
48
|
+
onLayout,
|
|
49
|
+
testID,
|
|
50
|
+
}: {
|
|
51
|
+
item: TabBarItem;
|
|
52
|
+
isActive: boolean;
|
|
53
|
+
size: TabBarProps['size'];
|
|
54
|
+
type: TabBarProps['type'];
|
|
55
|
+
iconPosition: TabBarProps['iconPosition'];
|
|
56
|
+
justify: TabBarProps['justify'];
|
|
57
|
+
onPress: () => void;
|
|
58
|
+
onLayout: (e: LayoutChangeEvent) => void;
|
|
59
|
+
testID?: string;
|
|
60
|
+
}) => {
|
|
61
|
+
const iconSize = ICON_SIZES[size || 'md'] || 18;
|
|
62
|
+
|
|
63
|
+
// Apply tab variants per tab (active/disabled differ per item)
|
|
64
|
+
tabBarTabStyles.useVariants({
|
|
65
|
+
size,
|
|
66
|
+
type,
|
|
67
|
+
active: isActive,
|
|
68
|
+
disabled: Boolean(item.disabled),
|
|
69
|
+
iconPosition,
|
|
70
|
+
justify,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Apply label variants
|
|
74
|
+
tabBarLabelStyles.useVariants({
|
|
75
|
+
size,
|
|
76
|
+
type,
|
|
77
|
+
active: isActive,
|
|
78
|
+
disabled: Boolean(item.disabled),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Apply icon variants
|
|
82
|
+
tabBarIconStyles.useVariants({
|
|
83
|
+
size,
|
|
84
|
+
disabled: Boolean(item.disabled),
|
|
85
|
+
iconPosition,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const icon = renderIcon(item.icon, isActive, iconSize);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<TouchableOpacity
|
|
92
|
+
onLayout={onLayout}
|
|
93
|
+
style={tabBarTabStyles.tab as any}
|
|
94
|
+
onPress={onPress}
|
|
95
|
+
disabled={item.disabled}
|
|
96
|
+
activeOpacity={0.7}
|
|
97
|
+
testID={`${testID}-tab-${item.value}`}
|
|
98
|
+
accessibilityRole="tab"
|
|
99
|
+
accessibilityLabel={item.label}
|
|
100
|
+
accessibilityState={{ selected: isActive, disabled: item.disabled }}
|
|
101
|
+
>
|
|
102
|
+
{icon && <View style={tabBarIconStyles.tabIcon as any}>{icon}</View>}
|
|
103
|
+
<Text style={tabBarLabelStyles.tabLabel as any}>{item.label}</Text>
|
|
104
|
+
</TouchableOpacity>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
37
108
|
const TabBar = forwardRef<IdealystElement, TabBarProps>(({
|
|
38
109
|
items,
|
|
39
110
|
value: controlledValue,
|
|
@@ -129,8 +200,10 @@ const TabBar = forwardRef<IdealystElement, TabBarProps>(({
|
|
|
129
200
|
};
|
|
130
201
|
});
|
|
131
202
|
|
|
132
|
-
// Apply container variants
|
|
203
|
+
// Apply container variants
|
|
133
204
|
tabBarContainerStyles.useVariants({
|
|
205
|
+
type,
|
|
206
|
+
pillMode,
|
|
134
207
|
justify,
|
|
135
208
|
gap,
|
|
136
209
|
padding,
|
|
@@ -141,9 +214,11 @@ const TabBar = forwardRef<IdealystElement, TabBarProps>(({
|
|
|
141
214
|
marginHorizontal,
|
|
142
215
|
});
|
|
143
216
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
217
|
+
// Apply indicator variants
|
|
218
|
+
tabBarIndicatorStyles.useVariants({
|
|
219
|
+
type,
|
|
220
|
+
pillMode,
|
|
221
|
+
});
|
|
147
222
|
|
|
148
223
|
return (
|
|
149
224
|
<ScrollView
|
|
@@ -155,11 +230,11 @@ const TabBar = forwardRef<IdealystElement, TabBarProps>(({
|
|
|
155
230
|
}}
|
|
156
231
|
style={{ width: '100%' }}
|
|
157
232
|
>
|
|
158
|
-
<View ref={ref as any} nativeID={id} style={[
|
|
233
|
+
<View ref={ref as any} nativeID={id} style={[tabBarContainerStyles.container as any, style]} testID={testID} {...nativeA11yProps}>
|
|
159
234
|
{/* Animated indicator - render first so it's behind */}
|
|
160
235
|
<Animated.View
|
|
161
236
|
style={[
|
|
162
|
-
|
|
237
|
+
tabBarIndicatorStyles.indicator as any,
|
|
163
238
|
indicatorAnimatedStyle,
|
|
164
239
|
]}
|
|
165
240
|
/>
|
|
@@ -168,40 +243,23 @@ const TabBar = forwardRef<IdealystElement, TabBarProps>(({
|
|
|
168
243
|
<View style={{ flexDirection: 'row', flex: 1 }}>
|
|
169
244
|
{items.map((item) => {
|
|
170
245
|
const isActive = value === item.value;
|
|
171
|
-
const iconSize = ICON_SIZES[size] || 18;
|
|
172
|
-
|
|
173
|
-
// Apply icon variants (size, disabled, iconPosition)
|
|
174
|
-
tabBarIconStyles.useVariants({
|
|
175
|
-
size,
|
|
176
|
-
disabled: Boolean(item.disabled),
|
|
177
|
-
iconPosition,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Compute dynamic styles for this tab - call as functions for theme reactivity
|
|
181
|
-
const tabStyle = (tabBarTabStyles.tab as any)({ type, size, active: isActive, pillMode, justify });
|
|
182
|
-
const labelStyle = (tabBarLabelStyles.tabLabel as any)({ type, active: isActive, pillMode });
|
|
183
|
-
|
|
184
|
-
const icon = renderIcon(item.icon, isActive, iconSize);
|
|
185
246
|
|
|
186
247
|
return (
|
|
187
|
-
<
|
|
248
|
+
<TabItem
|
|
188
249
|
key={item.value}
|
|
250
|
+
item={item}
|
|
251
|
+
isActive={isActive}
|
|
252
|
+
size={size}
|
|
253
|
+
type={type}
|
|
254
|
+
iconPosition={iconPosition}
|
|
255
|
+
justify={justify}
|
|
256
|
+
onPress={() => handleTabClick(item.value, item.disabled)}
|
|
189
257
|
onLayout={(event: LayoutChangeEvent) => {
|
|
190
258
|
const { x, width } = event.nativeEvent.layout;
|
|
191
259
|
handleTabLayout(item.value, x, width);
|
|
192
260
|
}}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
disabled={item.disabled}
|
|
196
|
-
activeOpacity={0.7}
|
|
197
|
-
testID={`${testID}-tab-${item.value}`}
|
|
198
|
-
accessibilityRole="tab"
|
|
199
|
-
accessibilityLabel={item.label}
|
|
200
|
-
accessibilityState={{ selected: isActive, disabled: item.disabled }}
|
|
201
|
-
>
|
|
202
|
-
{icon && <View style={tabBarIconStyles.tabIcon as any}>{icon}</View>}
|
|
203
|
-
<Text style={labelStyle}>{item.label}</Text>
|
|
204
|
-
</TouchableOpacity>
|
|
261
|
+
testID={testID}
|
|
262
|
+
/>
|
|
205
263
|
);
|
|
206
264
|
})}
|
|
207
265
|
</View>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TabBar styles using defineStyle with
|
|
2
|
+
* TabBar styles using defineStyle with static variants + compoundVariants.
|
|
3
3
|
*/
|
|
4
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
5
|
import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
|
|
6
|
-
import type { Theme as BaseTheme
|
|
6
|
+
import type { Theme as BaseTheme } from '@idealyst/theme';
|
|
7
7
|
import { ViewStyleSize } from '../utils/viewStyleProps';
|
|
8
8
|
|
|
9
9
|
// Required: Unistyles must see StyleSheet usage in original source to process this file
|
|
@@ -12,19 +12,14 @@ void StyleSheet;
|
|
|
12
12
|
// Wrap theme for $iterator support
|
|
13
13
|
type Theme = ThemeStyleWrapper<BaseTheme>;
|
|
14
14
|
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
type
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export type TabBarDynamicProps = {
|
|
21
|
-
size?: Size;
|
|
22
|
-
type?: TabBarType;
|
|
23
|
-
pillMode?: TabBarPillMode;
|
|
15
|
+
export type TabBarVariants = {
|
|
16
|
+
size?: ViewStyleSize;
|
|
17
|
+
type?: 'standard' | 'underline' | 'pills';
|
|
18
|
+
pillMode?: 'light' | 'dark';
|
|
24
19
|
active?: boolean;
|
|
25
20
|
disabled?: boolean;
|
|
26
|
-
iconPosition?:
|
|
27
|
-
justify?:
|
|
21
|
+
iconPosition?: 'left' | 'top';
|
|
22
|
+
justify?: 'start' | 'center' | 'equal' | 'space-between';
|
|
28
23
|
gap?: ViewStyleSize;
|
|
29
24
|
padding?: ViewStyleSize;
|
|
30
25
|
paddingVertical?: ViewStyleSize;
|
|
@@ -35,140 +30,158 @@ export type TabBarDynamicProps = {
|
|
|
35
30
|
};
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
|
-
* TabBar styles with
|
|
33
|
+
* TabBar styles with static variants and compoundVariants.
|
|
39
34
|
*/
|
|
40
35
|
export const tabBarStyles = defineStyle('TabBar', (theme: Theme) => ({
|
|
41
|
-
container:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
backgroundColor: backgroundColor || (type === 'pills' ? theme.colors.surface.secondary : undefined),
|
|
63
|
-
overflow: type === 'pills' ? ('hidden' as const) : undefined,
|
|
64
|
-
alignSelf: type === 'pills' ? ('flex-start' as const) : undefined,
|
|
65
|
-
width: type === 'pills' ? undefined : '100%',
|
|
66
|
-
borderRadius: type === 'pills' ? 9999 : undefined,
|
|
67
|
-
justifyContent: justifyContent as any,
|
|
68
|
-
variants: {
|
|
69
|
-
gap: {
|
|
70
|
-
gap: theme.sizes.$view.padding,
|
|
71
|
-
},
|
|
72
|
-
padding: {
|
|
73
|
-
padding: theme.sizes.$view.padding,
|
|
74
|
-
},
|
|
75
|
-
paddingVertical: {
|
|
76
|
-
paddingVertical: theme.sizes.$view.padding,
|
|
77
|
-
},
|
|
78
|
-
paddingHorizontal: {
|
|
79
|
-
paddingHorizontal: theme.sizes.$view.padding,
|
|
80
|
-
},
|
|
81
|
-
margin: {
|
|
82
|
-
margin: theme.sizes.$view.padding,
|
|
83
|
-
},
|
|
84
|
-
marginVertical: {
|
|
85
|
-
marginVertical: theme.sizes.$view.padding,
|
|
86
|
-
},
|
|
87
|
-
marginHorizontal: {
|
|
88
|
-
marginHorizontal: theme.sizes.$view.padding,
|
|
36
|
+
container: {
|
|
37
|
+
display: 'flex' as const,
|
|
38
|
+
flexDirection: 'row' as const,
|
|
39
|
+
position: 'relative' as const,
|
|
40
|
+
borderBottomWidth: 1,
|
|
41
|
+
borderBottomStyle: 'solid' as const,
|
|
42
|
+
borderBottomColor: theme.colors.border.primary,
|
|
43
|
+
width: '100%',
|
|
44
|
+
variants: {
|
|
45
|
+
type: {
|
|
46
|
+
standard: {},
|
|
47
|
+
underline: {},
|
|
48
|
+
pills: {
|
|
49
|
+
gap: 4,
|
|
50
|
+
borderBottomWidth: 0,
|
|
51
|
+
padding: 4,
|
|
52
|
+
backgroundColor: theme.colors.surface.secondary,
|
|
53
|
+
overflow: 'hidden' as const,
|
|
54
|
+
alignSelf: 'flex-start' as const,
|
|
55
|
+
width: undefined,
|
|
56
|
+
borderRadius: 9999,
|
|
89
57
|
},
|
|
90
58
|
},
|
|
91
|
-
|
|
59
|
+
justify: {
|
|
60
|
+
start: { justifyContent: 'flex-start' as const },
|
|
61
|
+
center: { justifyContent: 'center' as const },
|
|
62
|
+
equal: { justifyContent: 'stretch' as const },
|
|
63
|
+
'space-between': { justifyContent: 'space-between' as const },
|
|
64
|
+
},
|
|
65
|
+
gap: {
|
|
66
|
+
gap: theme.sizes.$view.padding,
|
|
67
|
+
},
|
|
68
|
+
padding: {
|
|
69
|
+
padding: theme.sizes.$view.padding,
|
|
70
|
+
},
|
|
71
|
+
paddingVertical: {
|
|
72
|
+
paddingVertical: theme.sizes.$view.padding,
|
|
73
|
+
},
|
|
74
|
+
paddingHorizontal: {
|
|
75
|
+
paddingHorizontal: theme.sizes.$view.padding,
|
|
76
|
+
},
|
|
77
|
+
margin: {
|
|
78
|
+
margin: theme.sizes.$view.padding,
|
|
79
|
+
},
|
|
80
|
+
marginVertical: {
|
|
81
|
+
marginVertical: theme.sizes.$view.padding,
|
|
82
|
+
},
|
|
83
|
+
marginHorizontal: {
|
|
84
|
+
marginHorizontal: theme.sizes.$view.padding,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
compoundVariants: [
|
|
88
|
+
{ type: 'pills', pillMode: 'dark', styles: { backgroundColor: theme.colors.surface.inverse } },
|
|
89
|
+
],
|
|
92
90
|
},
|
|
93
91
|
|
|
94
|
-
tab:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
: {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
fontWeight: '500' as const,
|
|
127
|
-
flex: justify === 'equal' ? 1 : undefined,
|
|
128
|
-
color,
|
|
129
|
-
position: 'relative' as const,
|
|
130
|
-
zIndex: 2,
|
|
131
|
-
backgroundColor: 'transparent' as const,
|
|
132
|
-
gap: 6,
|
|
133
|
-
borderRadius: type === 'pills' ? 9999 : undefined,
|
|
134
|
-
opacity: disabled ? 0.5 : 1,
|
|
135
|
-
...tabPadding,
|
|
136
|
-
variants: {
|
|
137
|
-
size: {
|
|
138
|
-
fontSize: theme.sizes.$tabBar.fontSize,
|
|
139
|
-
lineHeight: theme.sizes.$tabBar.lineHeight,
|
|
92
|
+
tab: {
|
|
93
|
+
display: 'flex' as const,
|
|
94
|
+
flexDirection: 'row' as const,
|
|
95
|
+
alignItems: 'center' as const,
|
|
96
|
+
justifyContent: 'center' as const,
|
|
97
|
+
fontWeight: '500' as const,
|
|
98
|
+
color: theme.colors.text.secondary,
|
|
99
|
+
position: 'relative' as const,
|
|
100
|
+
zIndex: 2,
|
|
101
|
+
backgroundColor: 'transparent' as const,
|
|
102
|
+
gap: 6,
|
|
103
|
+
opacity: 0.9,
|
|
104
|
+
variants: {
|
|
105
|
+
size: {
|
|
106
|
+
fontSize: theme.sizes.$tabBar.fontSize,
|
|
107
|
+
lineHeight: theme.sizes.$tabBar.lineHeight,
|
|
108
|
+
paddingTop: theme.sizes.$tabBar.padding,
|
|
109
|
+
paddingBottom: theme.sizes.$tabBar.padding,
|
|
110
|
+
paddingLeft: theme.sizes.$tabBar.padding,
|
|
111
|
+
paddingRight: theme.sizes.$tabBar.padding,
|
|
112
|
+
},
|
|
113
|
+
type: {
|
|
114
|
+
standard: {},
|
|
115
|
+
underline: {},
|
|
116
|
+
pills: {
|
|
117
|
+
borderRadius: 9999,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
active: {
|
|
121
|
+
true: {
|
|
122
|
+
color: theme.colors.text.primary,
|
|
123
|
+
opacity: 1,
|
|
140
124
|
},
|
|
125
|
+
false: {},
|
|
126
|
+
},
|
|
127
|
+
disabled: {
|
|
128
|
+
true: { opacity: 0.5 },
|
|
129
|
+
false: {},
|
|
130
|
+
},
|
|
131
|
+
iconPosition: {
|
|
132
|
+
top: { flexDirection: 'column' as const },
|
|
133
|
+
left: { flexDirection: 'row' as const },
|
|
141
134
|
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
_hover: disabled ? {} : { color: theme.colors.text.primary },
|
|
135
|
+
justify: {
|
|
136
|
+
start: {},
|
|
137
|
+
center: {},
|
|
138
|
+
equal: { flex: 1 },
|
|
139
|
+
'space-between': {},
|
|
148
140
|
},
|
|
149
|
-
}
|
|
141
|
+
},
|
|
142
|
+
compoundVariants: [
|
|
143
|
+
// Active + underline: use primary intent color
|
|
144
|
+
{ type: 'underline', active: true, styles: { color: theme.intents.primary.primary } },
|
|
145
|
+
// Active + pills: use contrast color
|
|
146
|
+
{ type: 'pills', active: true, styles: { color: theme.intents.primary.contrast } },
|
|
147
|
+
// Pills type: tighter padding per size (from theme)
|
|
148
|
+
{ type: 'pills', size: 'xs', styles: { paddingTop: theme.sizes.tabBar.xs.pillPaddingVertical, paddingBottom: theme.sizes.tabBar.xs.pillPaddingVertical, paddingLeft: theme.sizes.tabBar.xs.pillPaddingHorizontal, paddingRight: theme.sizes.tabBar.xs.pillPaddingHorizontal } },
|
|
149
|
+
{ type: 'pills', size: 'sm', styles: { paddingTop: theme.sizes.tabBar.sm.pillPaddingVertical, paddingBottom: theme.sizes.tabBar.sm.pillPaddingVertical, paddingLeft: theme.sizes.tabBar.sm.pillPaddingHorizontal, paddingRight: theme.sizes.tabBar.sm.pillPaddingHorizontal } },
|
|
150
|
+
{ type: 'pills', size: 'md', styles: { paddingTop: theme.sizes.tabBar.md.pillPaddingVertical, paddingBottom: theme.sizes.tabBar.md.pillPaddingVertical, paddingLeft: theme.sizes.tabBar.md.pillPaddingHorizontal, paddingRight: theme.sizes.tabBar.md.pillPaddingHorizontal } },
|
|
151
|
+
{ type: 'pills', size: 'lg', styles: { paddingTop: theme.sizes.tabBar.lg.pillPaddingVertical, paddingBottom: theme.sizes.tabBar.lg.pillPaddingVertical, paddingLeft: theme.sizes.tabBar.lg.pillPaddingHorizontal, paddingRight: theme.sizes.tabBar.lg.pillPaddingHorizontal } },
|
|
152
|
+
{ type: 'pills', size: 'xl', styles: { paddingTop: theme.sizes.tabBar.xl.pillPaddingVertical, paddingBottom: theme.sizes.tabBar.xl.pillPaddingVertical, paddingLeft: theme.sizes.tabBar.xl.pillPaddingHorizontal, paddingRight: theme.sizes.tabBar.xl.pillPaddingHorizontal } },
|
|
153
|
+
],
|
|
154
|
+
_web: {
|
|
155
|
+
border: 'none',
|
|
156
|
+
cursor: 'pointer',
|
|
157
|
+
outline: 'none',
|
|
158
|
+
transition: 'color 0.2s ease, opacity 0.2s ease',
|
|
159
|
+
},
|
|
150
160
|
},
|
|
151
161
|
|
|
152
|
-
tabLabel:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
zIndex: 3,
|
|
162
|
-
fontWeight: '500' as const,
|
|
163
|
-
color,
|
|
164
|
-
opacity: disabled ? 0.5 : 1,
|
|
165
|
-
variants: {
|
|
166
|
-
size: {
|
|
167
|
-
fontSize: theme.sizes.$tabBar.fontSize,
|
|
168
|
-
lineHeight: theme.sizes.$tabBar.lineHeight,
|
|
169
|
-
},
|
|
162
|
+
tabLabel: {
|
|
163
|
+
position: 'relative' as const,
|
|
164
|
+
zIndex: 3,
|
|
165
|
+
fontWeight: '500' as const,
|
|
166
|
+
color: theme.colors.text.secondary,
|
|
167
|
+
variants: {
|
|
168
|
+
size: {
|
|
169
|
+
fontSize: theme.sizes.$tabBar.fontSize,
|
|
170
|
+
lineHeight: theme.sizes.$tabBar.lineHeight,
|
|
170
171
|
},
|
|
171
|
-
|
|
172
|
+
active: {
|
|
173
|
+
true: { color: theme.colors.text.primary },
|
|
174
|
+
false: {},
|
|
175
|
+
},
|
|
176
|
+
disabled: {
|
|
177
|
+
true: { opacity: 0.5 },
|
|
178
|
+
false: { opacity: 1 },
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
compoundVariants: [
|
|
182
|
+
{ type: 'underline', active: true, styles: { color: theme.intents.primary.primary } },
|
|
183
|
+
{ type: 'pills', active: true, styles: { color: theme.colors.text.primary } },
|
|
184
|
+
],
|
|
172
185
|
},
|
|
173
186
|
|
|
174
187
|
tabIcon: {
|
|
@@ -191,31 +204,33 @@ export const tabBarStyles = defineStyle('TabBar', (theme: Theme) => ({
|
|
|
191
204
|
},
|
|
192
205
|
},
|
|
193
206
|
|
|
194
|
-
indicator:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
207
|
+
indicator: {
|
|
208
|
+
position: 'absolute' as const,
|
|
209
|
+
pointerEvents: 'none' as const,
|
|
210
|
+
zIndex: 1,
|
|
211
|
+
backgroundColor: theme.intents.primary.primary,
|
|
212
|
+
bottom: -1,
|
|
213
|
+
height: 2,
|
|
214
|
+
variants: {
|
|
215
|
+
type: {
|
|
216
|
+
standard: {},
|
|
217
|
+
underline: {},
|
|
218
|
+
pills: {
|
|
219
|
+
borderRadius: 9999,
|
|
220
|
+
top: 4,
|
|
221
|
+
bottom: 4,
|
|
222
|
+
height: undefined,
|
|
223
|
+
left: 0,
|
|
224
|
+
backgroundColor: theme.colors.surface.tertiary,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
compoundVariants: [
|
|
229
|
+
{ type: 'pills', pillMode: 'dark', styles: { backgroundColor: theme.colors.surface.secondary } },
|
|
230
|
+
],
|
|
231
|
+
_web: {
|
|
232
|
+
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
233
|
+
},
|
|
219
234
|
},
|
|
220
235
|
}));
|
|
221
236
|
|
|
@@ -66,29 +66,33 @@ const Tab: React.FC<TabProps> = ({
|
|
|
66
66
|
}) => {
|
|
67
67
|
const iconSize = ICON_SIZES[size || 'md'] || 18;
|
|
68
68
|
|
|
69
|
-
// Apply tab variants
|
|
69
|
+
// Apply tab variants
|
|
70
70
|
tabBarTabStyles.useVariants({
|
|
71
71
|
size,
|
|
72
|
+
type,
|
|
73
|
+
active: isActive,
|
|
74
|
+
disabled: Boolean(item.disabled),
|
|
75
|
+
iconPosition,
|
|
76
|
+
justify,
|
|
72
77
|
});
|
|
73
78
|
|
|
74
|
-
// Apply label variants
|
|
79
|
+
// Apply label variants
|
|
75
80
|
tabBarLabelStyles.useVariants({
|
|
76
81
|
size,
|
|
82
|
+
type,
|
|
83
|
+
active: isActive,
|
|
84
|
+
disabled: Boolean(item.disabled),
|
|
77
85
|
});
|
|
78
86
|
|
|
79
|
-
// Apply icon variants
|
|
87
|
+
// Apply icon variants
|
|
80
88
|
tabBarIconStyles.useVariants({
|
|
81
89
|
size,
|
|
82
90
|
disabled: Boolean(item.disabled),
|
|
83
91
|
iconPosition,
|
|
84
92
|
});
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const labelStyle = (tabBarLabelStyles.tabLabel as any)({ type, active: isActive, pillMode });
|
|
89
|
-
|
|
90
|
-
const tabProps = getWebProps([tabStyle]);
|
|
91
|
-
const labelProps = getWebProps([labelStyle]);
|
|
94
|
+
const tabProps = getWebProps([tabBarTabStyles.tab as any]);
|
|
95
|
+
const labelProps = getWebProps([tabBarLabelStyles.tabLabel as any]);
|
|
92
96
|
const iconProps = getWebProps([tabBarIconStyles.tabIcon as any]);
|
|
93
97
|
|
|
94
98
|
// Merge refs from getWebProps with our tracking ref
|
|
@@ -262,8 +266,10 @@ const TabBar: React.FC<TabBarProps> = ({
|
|
|
262
266
|
onChange?.(itemValue);
|
|
263
267
|
};
|
|
264
268
|
|
|
265
|
-
// Apply container variants
|
|
266
|
-
|
|
269
|
+
// Apply container variants
|
|
270
|
+
tabBarContainerStyles.useVariants({
|
|
271
|
+
type,
|
|
272
|
+
pillMode,
|
|
267
273
|
justify,
|
|
268
274
|
gap,
|
|
269
275
|
padding,
|
|
@@ -274,12 +280,14 @@ const TabBar: React.FC<TabBarProps> = ({
|
|
|
274
280
|
marginHorizontal,
|
|
275
281
|
});
|
|
276
282
|
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
283
|
+
// Apply indicator variants
|
|
284
|
+
tabBarIndicatorStyles.useVariants({
|
|
285
|
+
type,
|
|
286
|
+
pillMode,
|
|
287
|
+
});
|
|
280
288
|
|
|
281
|
-
const
|
|
282
|
-
const indicatorProps = getWebProps([
|
|
289
|
+
const containerProps = getWebProps([tabBarContainerStyles.container as any, style as any]);
|
|
290
|
+
const indicatorProps = getWebProps([tabBarIndicatorStyles.indicator as any]);
|
|
283
291
|
|
|
284
292
|
// Merge container ref with getWebProps ref
|
|
285
293
|
const mergedContainerRef = useMergeRefs<HTMLDivElement>(
|