@retray-dev/ui-kit 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/COMPONENTS.md +264 -15
  2. package/README.md +7 -6
  3. package/dist/index.d.mts +114 -11
  4. package/dist/index.d.ts +114 -11
  5. package/dist/index.js +660 -134
  6. package/dist/index.mjs +656 -138
  7. package/package.json +8 -8
  8. package/src/components/Accordion/Accordion.tsx +4 -4
  9. package/src/components/Alert/Alert.tsx +32 -8
  10. package/src/components/Alert/index.ts +2 -2
  11. package/src/components/Avatar/Avatar.tsx +8 -8
  12. package/src/components/Badge/Badge.tsx +4 -4
  13. package/src/components/Button/Button.tsx +21 -14
  14. package/src/components/Card/Card.tsx +9 -9
  15. package/src/components/Checkbox/Checkbox.tsx +8 -8
  16. package/src/components/Chip/Chip.tsx +142 -0
  17. package/src/components/Chip/index.ts +2 -0
  18. package/src/components/ConfirmDialog/ConfirmDialog.tsx +87 -0
  19. package/src/components/ConfirmDialog/index.ts +2 -0
  20. package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +48 -0
  21. package/src/components/CurrencyDisplay/index.ts +1 -0
  22. package/src/components/CurrencyInputLarge/CurrencyInputLarge.tsx +66 -0
  23. package/src/components/CurrencyInputLarge/index.ts +1 -0
  24. package/src/components/EmptyState/EmptyState.tsx +40 -6
  25. package/src/components/Input/Input.tsx +8 -8
  26. package/src/components/LabelValue/LabelValue.tsx +47 -0
  27. package/src/components/LabelValue/index.ts +2 -0
  28. package/src/components/ListItem/ListItem.tsx +121 -0
  29. package/src/components/ListItem/index.ts +2 -0
  30. package/src/components/MonthPicker/MonthPicker.tsx +92 -0
  31. package/src/components/MonthPicker/index.ts +2 -0
  32. package/src/components/Select/Select.tsx +19 -19
  33. package/src/components/Switch/Switch.tsx +12 -7
  34. package/src/components/Tabs/Tabs.tsx +34 -15
  35. package/src/components/Text/Text.tsx +6 -6
  36. package/src/components/Textarea/Textarea.tsx +9 -9
  37. package/src/components/Toast/Toast.tsx +25 -7
  38. package/src/components/Toggle/Toggle.tsx +93 -24
  39. package/src/index.ts +7 -0
@@ -0,0 +1,66 @@
1
+ import React from 'react'
2
+ import { ViewStyle } from 'react-native'
3
+ import { Input } from '../Input'
4
+
5
+ export interface CurrencyInputLargeProps {
6
+ value?: string
7
+ onChangeText?: (formatted: string) => void
8
+ /** Called with the parsed numeric value (no separators, no prefix). */
9
+ onChangeValue?: (raw: number) => void
10
+ /** Symbol prepended to the formatted value. Defaults to `'$'`. */
11
+ prefix?: string
12
+ /** Character used to separate groups of three digits. Defaults to `'.'`. */
13
+ thousandsSeparator?: '.' | ','
14
+ label?: string
15
+ /** Red helper text; also changes input border to destructive color. */
16
+ error?: string
17
+ hint?: string
18
+ placeholder?: string
19
+ editable?: boolean
20
+ containerStyle?: ViewStyle
21
+ }
22
+
23
+ function formatCurrency(raw: string, separator: '.' | ','): string {
24
+ const digits = raw.replace(/\D/g, '')
25
+ if (!digits) return ''
26
+ return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator)
27
+ }
28
+
29
+ export function CurrencyInputLarge({
30
+ value,
31
+ onChangeText,
32
+ onChangeValue,
33
+ prefix = '$',
34
+ thousandsSeparator = '.',
35
+ label,
36
+ error,
37
+ hint,
38
+ placeholder,
39
+ editable,
40
+ containerStyle,
41
+ }: CurrencyInputLargeProps) {
42
+ const handleChange = (text: string) => {
43
+ const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text
44
+ const formatted = formatCurrency(withoutPrefix, thousandsSeparator)
45
+ const display = formatted ? `${prefix}${formatted}` : ''
46
+ onChangeText?.(display)
47
+ const separatorRegex = new RegExp(`\\${thousandsSeparator}`, 'g')
48
+ const raw = parseFloat(formatted.replace(separatorRegex, '') || '0')
49
+ onChangeValue?.(isNaN(raw) ? 0 : raw)
50
+ }
51
+
52
+ return (
53
+ <Input
54
+ value={value}
55
+ onChangeText={handleChange}
56
+ keyboardType="numeric"
57
+ label={label}
58
+ error={error}
59
+ hint={hint}
60
+ placeholder={placeholder ?? `${prefix}0`}
61
+ editable={editable}
62
+ containerStyle={containerStyle}
63
+ style={{ fontSize: 36 }}
64
+ />
65
+ )
66
+ }
@@ -0,0 +1 @@
1
+ export * from './CurrencyInputLarge'
@@ -7,24 +7,46 @@ export interface EmptyStateProps {
7
7
  title: string
8
8
  description?: string
9
9
  action?: React.ReactNode
10
+ /** `compact` hides description/action and uses tighter spacing and a smaller icon. */
11
+ size?: 'default' | 'compact'
10
12
  style?: ViewStyle
11
13
  }
12
14
 
13
- export function EmptyState({ icon, title, description, action, style }: EmptyStateProps) {
15
+ export function EmptyState({ icon, title, description, action, size = 'default', style }: EmptyStateProps) {
14
16
  const { colors } = useTheme()
17
+ const isCompact = size === 'compact'
15
18
 
16
19
  return (
17
- <View style={[styles.container, { borderColor: colors.border }, style]}>
20
+ <View
21
+ style={[
22
+ styles.container,
23
+ isCompact && styles.containerCompact,
24
+ { borderColor: colors.border },
25
+ style,
26
+ ]}
27
+ >
18
28
  {icon ? (
19
- <View style={[styles.iconWrapper, { backgroundColor: colors.muted }]}>{icon}</View>
29
+ <View
30
+ style={[
31
+ styles.iconWrapper,
32
+ isCompact && styles.iconWrapperCompact,
33
+ { backgroundColor: colors.muted },
34
+ ]}
35
+ >
36
+ {icon}
37
+ </View>
20
38
  ) : null}
21
39
  <View style={styles.textWrapper}>
22
- <Text style={[styles.title, { color: colors.foreground }]}>{title}</Text>
23
- {description ? (
40
+ <Text
41
+ style={[styles.title, isCompact && styles.titleCompact, { color: colors.foreground }]}
42
+ >
43
+ {title}
44
+ </Text>
45
+ {description && !isCompact ? (
24
46
  <Text style={[styles.description, { color: colors.mutedForeground }]}>{description}</Text>
25
47
  ) : null}
26
48
  </View>
27
- {action ? <View style={styles.action}>{action}</View> : null}
49
+ {action && !isCompact ? <View style={styles.action}>{action}</View> : null}
28
50
  </View>
29
51
  )
30
52
  }
@@ -39,6 +61,10 @@ const styles = StyleSheet.create({
39
61
  padding: 32,
40
62
  gap: 16,
41
63
  },
64
+ containerCompact: {
65
+ padding: 20,
66
+ gap: 10,
67
+ },
42
68
  iconWrapper: {
43
69
  width: 48,
44
70
  height: 48,
@@ -46,6 +72,11 @@ const styles = StyleSheet.create({
46
72
  alignItems: 'center',
47
73
  justifyContent: 'center',
48
74
  },
75
+ iconWrapperCompact: {
76
+ width: 36,
77
+ height: 36,
78
+ borderRadius: 8,
79
+ },
49
80
  textWrapper: {
50
81
  alignItems: 'center',
51
82
  gap: 8,
@@ -56,6 +87,9 @@ const styles = StyleSheet.create({
56
87
  fontWeight: '500',
57
88
  textAlign: 'center',
58
89
  },
90
+ titleCompact: {
91
+ fontSize: 15,
92
+ },
59
93
  description: {
60
94
  fontSize: 14,
61
95
  lineHeight: 20,
@@ -53,21 +53,21 @@ export function Input({ label, error, hint, containerStyle, style, onFocus, onBl
53
53
 
54
54
  const styles = StyleSheet.create({
55
55
  container: {
56
- gap: 4,
56
+ gap: 6,
57
57
  },
58
58
  label: {
59
- fontSize: 14,
59
+ fontSize: 15,
60
60
  fontWeight: '500',
61
- marginBottom: 4,
61
+ marginBottom: 6,
62
62
  },
63
63
  input: {
64
64
  borderWidth: 1.5,
65
- borderRadius: 8,
66
- paddingHorizontal: 16,
67
- paddingVertical: 14,
68
- fontSize: 16,
65
+ borderRadius: 14,
66
+ paddingHorizontal: 20,
67
+ paddingVertical: 16,
68
+ fontSize: 17,
69
69
  },
70
70
  helperText: {
71
- fontSize: 12,
71
+ fontSize: 13,
72
72
  },
73
73
  })
@@ -0,0 +1,47 @@
1
+ import React from 'react'
2
+ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
+ import { useTheme } from '../../theme'
4
+
5
+ export interface LabelValueProps {
6
+ label: string
7
+ value: string | React.ReactNode
8
+ style?: ViewStyle
9
+ }
10
+
11
+ export function LabelValue({ label, value, style }: LabelValueProps) {
12
+ const { colors } = useTheme()
13
+
14
+ return (
15
+ <View style={[styles.container, style]}>
16
+ <Text style={[styles.label, { color: colors.mutedForeground }]} allowFontScaling={true}>
17
+ {label}
18
+ </Text>
19
+ {typeof value === 'string' ? (
20
+ <Text style={[styles.value, { color: colors.foreground }]} allowFontScaling={true}>
21
+ {value}
22
+ </Text>
23
+ ) : (
24
+ value
25
+ )}
26
+ </View>
27
+ )
28
+ }
29
+
30
+ const styles = StyleSheet.create({
31
+ container: {
32
+ flexDirection: 'row',
33
+ justifyContent: 'space-between',
34
+ alignItems: 'center',
35
+ gap: 12,
36
+ },
37
+ label: {
38
+ fontSize: 13,
39
+ lineHeight: 18,
40
+ },
41
+ value: {
42
+ fontSize: 15,
43
+ fontWeight: '500',
44
+ lineHeight: 22,
45
+ textAlign: 'right',
46
+ },
47
+ })
@@ -0,0 +1,2 @@
1
+ export { LabelValue } from './LabelValue'
2
+ export type { LabelValueProps } from './LabelValue'
@@ -0,0 +1,121 @@
1
+ import React, { useRef } from 'react'
2
+ import {
3
+ TouchableOpacity,
4
+ Animated,
5
+ View,
6
+ Text,
7
+ StyleSheet,
8
+ ViewStyle,
9
+ Platform,
10
+ } from 'react-native'
11
+ import * as Haptics from 'expo-haptics'
12
+ import { useTheme } from '../../theme'
13
+
14
+ const nativeDriver = Platform.OS !== 'web'
15
+
16
+ export interface ListItemProps {
17
+ icon?: React.ReactNode
18
+ title: string
19
+ subtitle?: string
20
+ trailing?: string | React.ReactNode
21
+ onPress?: () => void
22
+ disabled?: boolean
23
+ style?: ViewStyle
24
+ }
25
+
26
+ export function ListItem({ icon, title, subtitle, trailing, onPress, disabled, style }: ListItemProps) {
27
+ const { colors } = useTheme()
28
+ const scale = useRef(new Animated.Value(1)).current
29
+
30
+ const handlePressIn = () => {
31
+ if (!onPress || disabled) return
32
+ Animated.spring(scale, {
33
+ toValue: 0.97,
34
+ useNativeDriver: nativeDriver,
35
+ speed: 40,
36
+ bounciness: 0,
37
+ }).start()
38
+ }
39
+
40
+ const handlePressOut = () => {
41
+ Animated.spring(scale, {
42
+ toValue: 1,
43
+ useNativeDriver: nativeDriver,
44
+ speed: 40,
45
+ bounciness: 4,
46
+ }).start()
47
+ }
48
+
49
+ const handlePress = () => {
50
+ Haptics.selectionAsync()
51
+ onPress?.()
52
+ }
53
+
54
+ return (
55
+ <Animated.View style={[{ transform: [{ scale }] }, disabled && styles.disabled]}>
56
+ <TouchableOpacity
57
+ style={[styles.container, style]}
58
+ onPress={onPress ? handlePress : undefined}
59
+ onPressIn={handlePressIn}
60
+ onPressOut={handlePressOut}
61
+ disabled={disabled}
62
+ activeOpacity={1}
63
+ touchSoundDisabled={true}
64
+ >
65
+ {icon ? <View style={styles.iconWrapper}>{icon}</View> : null}
66
+ <View style={styles.content}>
67
+ <Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>
68
+ {title}
69
+ </Text>
70
+ {subtitle ? (
71
+ <Text style={[styles.subtitle, { color: colors.mutedForeground }]} allowFontScaling={true}>
72
+ {subtitle}
73
+ </Text>
74
+ ) : null}
75
+ </View>
76
+ {trailing !== undefined ? (
77
+ typeof trailing === 'string' ? (
78
+ <Text style={[styles.trailing, { color: colors.mutedForeground }]} allowFontScaling={true}>
79
+ {trailing}
80
+ </Text>
81
+ ) : (
82
+ trailing
83
+ )
84
+ ) : null}
85
+ </TouchableOpacity>
86
+ </Animated.View>
87
+ )
88
+ }
89
+
90
+ const styles = StyleSheet.create({
91
+ container: {
92
+ flexDirection: 'row',
93
+ alignItems: 'center',
94
+ paddingHorizontal: 16,
95
+ paddingVertical: 14,
96
+ gap: 12,
97
+ },
98
+ iconWrapper: {
99
+ alignItems: 'center',
100
+ justifyContent: 'center',
101
+ },
102
+ content: {
103
+ flex: 1,
104
+ gap: 3,
105
+ },
106
+ title: {
107
+ fontSize: 16,
108
+ fontWeight: '500',
109
+ lineHeight: 22,
110
+ },
111
+ subtitle: {
112
+ fontSize: 13,
113
+ lineHeight: 18,
114
+ },
115
+ trailing: {
116
+ fontSize: 15,
117
+ },
118
+ disabled: {
119
+ opacity: 0.45,
120
+ },
121
+ })
@@ -0,0 +1,2 @@
1
+ export { ListItem } from './ListItem'
2
+ export type { ListItemProps } from './ListItem'
@@ -0,0 +1,92 @@
1
+ import React from 'react'
2
+ import { View, Text, TouchableOpacity, StyleSheet, ViewStyle } from 'react-native'
3
+ import * as Haptics from 'expo-haptics'
4
+ import { useTheme } from '../../theme'
5
+
6
+ const MONTH_NAMES = [
7
+ 'January', 'February', 'March', 'April', 'May', 'June',
8
+ 'July', 'August', 'September', 'October', 'November', 'December',
9
+ ]
10
+
11
+ export interface MonthPickerValue {
12
+ /** Month number 1–12 */
13
+ month: number
14
+ year: number
15
+ }
16
+
17
+ export interface MonthPickerProps {
18
+ value: MonthPickerValue
19
+ onChange: (value: MonthPickerValue) => void
20
+ style?: ViewStyle
21
+ }
22
+
23
+ export function MonthPicker({ value, onChange, style }: MonthPickerProps) {
24
+ const { colors } = useTheme()
25
+
26
+ const handlePrev = () => {
27
+ Haptics.selectionAsync()
28
+ if (value.month === 1) {
29
+ onChange({ month: 12, year: value.year - 1 })
30
+ } else {
31
+ onChange({ month: value.month - 1, year: value.year })
32
+ }
33
+ }
34
+
35
+ const handleNext = () => {
36
+ Haptics.selectionAsync()
37
+ if (value.month === 12) {
38
+ onChange({ month: 1, year: value.year + 1 })
39
+ } else {
40
+ onChange({ month: value.month + 1, year: value.year })
41
+ }
42
+ }
43
+
44
+ return (
45
+ <View style={[styles.container, style]}>
46
+ <TouchableOpacity
47
+ style={styles.arrow}
48
+ onPress={handlePrev}
49
+ activeOpacity={0.6}
50
+ touchSoundDisabled={true}
51
+ >
52
+ <Text style={[styles.arrowText, { color: colors.foreground }]}>‹</Text>
53
+ </TouchableOpacity>
54
+ <Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>
55
+ {MONTH_NAMES[value.month - 1]} {value.year}
56
+ </Text>
57
+ <TouchableOpacity
58
+ style={styles.arrow}
59
+ onPress={handleNext}
60
+ activeOpacity={0.6}
61
+ touchSoundDisabled={true}
62
+ >
63
+ <Text style={[styles.arrowText, { color: colors.foreground }]}>›</Text>
64
+ </TouchableOpacity>
65
+ </View>
66
+ )
67
+ }
68
+
69
+ const styles = StyleSheet.create({
70
+ container: {
71
+ flexDirection: 'row',
72
+ alignItems: 'center',
73
+ justifyContent: 'space-between',
74
+ },
75
+ arrow: {
76
+ width: 44,
77
+ height: 44,
78
+ alignItems: 'center',
79
+ justifyContent: 'center',
80
+ },
81
+ arrowText: {
82
+ fontSize: 24,
83
+ lineHeight: 30,
84
+ },
85
+ label: {
86
+ fontSize: 17,
87
+ fontWeight: '500',
88
+ lineHeight: 24,
89
+ textAlign: 'center',
90
+ minWidth: 160,
91
+ },
92
+ })
@@ -0,0 +1,2 @@
1
+ export { MonthPicker } from './MonthPicker'
2
+ export type { MonthPickerProps, MonthPickerValue } from './MonthPicker'
@@ -163,10 +163,10 @@ export function Select({
163
163
 
164
164
  const styles = StyleSheet.create({
165
165
  container: {
166
- gap: 4,
166
+ gap: 6,
167
167
  },
168
168
  label: {
169
- fontSize: 14,
169
+ fontSize: 15,
170
170
  fontWeight: '500',
171
171
  marginBottom: 2,
172
172
  },
@@ -175,24 +175,24 @@ const styles = StyleSheet.create({
175
175
  alignItems: 'center',
176
176
  justifyContent: 'space-between',
177
177
  borderWidth: 1.5,
178
- borderRadius: 8,
179
- paddingHorizontal: 16,
180
- paddingVertical: 14,
178
+ borderRadius: 14,
179
+ paddingHorizontal: 20,
180
+ paddingVertical: 16,
181
181
  },
182
182
  triggerText: {
183
- fontSize: 16,
183
+ fontSize: 17,
184
184
  flex: 1,
185
185
  },
186
186
  chevron: {
187
- fontSize: 14,
187
+ fontSize: 16,
188
188
  marginLeft: 8,
189
189
  },
190
190
  helperText: {
191
- fontSize: 12,
191
+ fontSize: 13,
192
192
  },
193
193
  sheetBackground: {
194
- borderTopLeftRadius: 16,
195
- borderTopRightRadius: 16,
194
+ borderTopLeftRadius: 24,
195
+ borderTopRightRadius: 24,
196
196
  },
197
197
  sheetHandle: {
198
198
  width: 36,
@@ -200,32 +200,32 @@ const styles = StyleSheet.create({
200
200
  borderRadius: 2,
201
201
  },
202
202
  sheetContent: {
203
- paddingHorizontal: 16,
204
- paddingBottom: 32,
203
+ paddingHorizontal: 20,
204
+ paddingBottom: 36,
205
205
  },
206
206
  sheetTitle: {
207
- fontSize: 16,
207
+ fontSize: 17,
208
208
  fontWeight: '600',
209
- paddingVertical: 12,
209
+ paddingVertical: 16,
210
210
  paddingHorizontal: 4,
211
211
  },
212
212
  option: {
213
213
  flexDirection: 'row',
214
214
  alignItems: 'center',
215
215
  justifyContent: 'space-between',
216
- paddingHorizontal: 12,
217
- paddingVertical: 14,
218
- borderRadius: 8,
216
+ paddingHorizontal: 16,
217
+ paddingVertical: 16,
218
+ borderRadius: 12,
219
219
  },
220
220
  optionText: {
221
- fontSize: 15,
221
+ fontSize: 17,
222
222
  flex: 1,
223
223
  },
224
224
  disabledOption: {
225
225
  opacity: 0.45,
226
226
  },
227
227
  checkmark: {
228
- fontSize: 14,
228
+ fontSize: 16,
229
229
  fontWeight: '600',
230
230
  marginLeft: 8,
231
231
  },
@@ -1,11 +1,13 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
- import { TouchableOpacity, Animated, StyleSheet, ViewStyle } from 'react-native'
2
+ import { TouchableOpacity, Animated, StyleSheet, ViewStyle, Platform } from 'react-native'
3
+
4
+ const nativeDriver = Platform.OS !== 'web'
3
5
  import * as Haptics from 'expo-haptics'
4
6
  import { useTheme } from '../../theme'
5
7
 
6
- const TRACK_WIDTH = 56
7
- const TRACK_HEIGHT = 32
8
- const THUMB_SIZE = 24
8
+ const TRACK_WIDTH = 60
9
+ const TRACK_HEIGHT = 36
10
+ const THUMB_SIZE = 28
9
11
  const THUMB_OFFSET = 4
10
12
  const THUMB_TRAVEL = TRACK_WIDTH - THUMB_SIZE - THUMB_OFFSET * 2
11
13
 
@@ -25,7 +27,7 @@ export function Switch({ checked = false, onCheckedChange, disabled, style }: Sw
25
27
  Animated.parallel([
26
28
  Animated.spring(translateX, {
27
29
  toValue: checked ? THUMB_TRAVEL : 0,
28
- useNativeDriver: true,
30
+ useNativeDriver: nativeDriver,
29
31
  bounciness: 4,
30
32
  }),
31
33
  Animated.timing(trackOpacity, {
@@ -70,10 +72,13 @@ const styles = StyleSheet.create({
70
72
  width: TRACK_WIDTH,
71
73
  height: TRACK_HEIGHT,
72
74
  borderRadius: TRACK_HEIGHT / 2,
73
- justifyContent: 'center',
74
- paddingHorizontal: THUMB_OFFSET,
75
+ // No justifyContent/alignItems — thumb uses absolute positioning
76
+ // so the track's flex layout doesn't interfere with translateX animation
75
77
  },
76
78
  thumb: {
79
+ position: 'absolute',
80
+ top: THUMB_OFFSET,
81
+ left: THUMB_OFFSET,
77
82
  width: THUMB_SIZE,
78
83
  height: THUMB_SIZE,
79
84
  borderRadius: THUMB_SIZE / 2,
@@ -6,6 +6,7 @@ import { useTheme } from '../../theme'
6
6
  export interface TabItem {
7
7
  label: string
8
8
  value: string
9
+ icon?: React.ReactNode
9
10
  }
10
11
 
11
12
  export interface TabsProps {
@@ -60,15 +61,22 @@ function TabTrigger({
60
61
  touchSoundDisabled={true}
61
62
  >
62
63
  <Animated.View style={{ transform: [{ scale }] }}>
63
- <Text
64
- style={[
65
- styles.triggerLabel,
66
- { color: isActive ? colors.foreground : colors.mutedForeground },
67
- isActive && styles.activeTriggerLabel,
68
- ]}
69
- >
70
- {tab.label}
71
- </Text>
64
+ <View style={styles.triggerInner}>
65
+ {tab.icon ? (
66
+ <View style={styles.triggerIcon}>
67
+ {(typeof tab.icon === 'function' ? (tab.icon as any)(isActive) : tab.icon) as React.ReactNode}
68
+ </View>
69
+ ) : null}
70
+ <Text
71
+ style={[
72
+ styles.triggerLabel,
73
+ { color: isActive ? colors.foreground : colors.mutedForeground },
74
+ isActive && styles.activeTriggerLabel,
75
+ ]}
76
+ >
77
+ {tab.label}
78
+ </Text>
79
+ </View>
72
80
  </Animated.View>
73
81
  </TouchableOpacity>
74
82
  )
@@ -133,7 +141,7 @@ export function Tabs({ tabs, value, onValueChange, children, style }: TabsProps)
133
141
  bottom: 4,
134
142
  left: pillX,
135
143
  width: pillWidth,
136
- borderRadius: 6,
144
+ borderRadius: 8,
137
145
  shadowColor: '#000',
138
146
  shadowOffset: { width: 0, height: 1 },
139
147
  shadowOpacity: 0.1,
@@ -172,22 +180,33 @@ export function TabsContent({ value, activeValue, children, style }: TabsContent
172
180
  const styles = StyleSheet.create({
173
181
  list: {
174
182
  flexDirection: 'row',
175
- borderRadius: 8,
183
+ borderRadius: 12,
176
184
  padding: 4,
177
185
  gap: 4,
178
186
  },
179
187
  pill: {},
180
188
  trigger: {
181
189
  flex: 1,
182
- paddingVertical: 8,
183
- paddingHorizontal: 12,
184
- borderRadius: 6,
190
+ paddingVertical: 12,
191
+ paddingHorizontal: 16,
192
+ borderRadius: 8,
185
193
  alignItems: 'center',
186
194
  justifyContent: 'center',
187
195
  zIndex: 1,
188
196
  },
197
+ triggerInner: {
198
+ flexDirection: 'row',
199
+ alignItems: 'center',
200
+ justifyContent: 'center',
201
+ gap: 8,
202
+ },
203
+ triggerIcon: {
204
+ marginRight: 6,
205
+ alignItems: 'center',
206
+ justifyContent: 'center',
207
+ },
189
208
  triggerLabel: {
190
- fontSize: 14,
209
+ fontSize: 15,
191
210
  fontWeight: '400',
192
211
  },
193
212
  activeTriggerLabel: {