@retray-dev/ui-kit 1.8.0 → 2.5.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 (34) hide show
  1. package/COMPONENTS.md +150 -43
  2. package/dist/index.d.mts +80 -44
  3. package/dist/index.d.ts +80 -44
  4. package/dist/index.js +627 -457
  5. package/dist/index.mjs +626 -457
  6. package/package.json +8 -2
  7. package/src/components/Accordion/Accordion.tsx +4 -6
  8. package/src/components/Alert/Alert.tsx +3 -3
  9. package/src/components/AlertBanner/AlertBanner.tsx +85 -0
  10. package/src/components/{Alert → AlertBanner}/index.ts +2 -2
  11. package/src/components/Avatar/Avatar.tsx +1 -0
  12. package/src/components/Badge/Badge.tsx +45 -9
  13. package/src/components/Button/Button.tsx +5 -5
  14. package/src/components/Card/Card.tsx +90 -18
  15. package/src/components/Checkbox/Checkbox.tsx +4 -4
  16. package/src/components/Chip/Chip.tsx +36 -5
  17. package/src/components/CurrencyInput/CurrencyInput.tsx +9 -1
  18. package/src/components/EmptyState/EmptyState.tsx +2 -1
  19. package/src/components/Input/Input.tsx +107 -26
  20. package/src/components/ListItem/ListItem.tsx +157 -21
  21. package/src/components/MonthPicker/MonthPicker.tsx +3 -6
  22. package/src/components/RadioGroup/RadioGroup.tsx +2 -2
  23. package/src/components/Select/Select.tsx +200 -132
  24. package/src/components/Slider/Slider.tsx +64 -100
  25. package/src/components/Switch/Switch.tsx +22 -20
  26. package/src/components/Textarea/Textarea.tsx +16 -7
  27. package/src/components/Toast/Toast.tsx +23 -18
  28. package/src/components/Toggle/Toggle.tsx +36 -49
  29. package/src/index.ts +3 -2
  30. package/src/theme/ThemeProvider.tsx +11 -8
  31. package/src/theme/colors.ts +19 -18
  32. package/src/theme/types.ts +2 -0
  33. package/src/components/CurrencyInputLarge/CurrencyInputLarge.tsx +0 -66
  34. package/src/components/CurrencyInputLarge/index.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retray-dev/ui-kit",
3
- "version": "1.8.0",
3
+ "version": "2.5.0",
4
4
  "description": "Personal UI Kit for React Native / Expo",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -45,7 +45,10 @@
45
45
  "react-native-reanimated": ">=4.0.0",
46
46
  "react-native-gesture-handler": ">=2.0.0",
47
47
  "react-native-worklets": ">=0.8.0",
48
- "react-native-safe-area-context": ">=4.0.0"
48
+ "react-native-safe-area-context": ">=4.0.0",
49
+ "@react-native-picker/picker": ">=2.0.0",
50
+ "@react-native-community/slider": ">=4.0.0",
51
+ "@expo/vector-icons": ">=14.0.0"
49
52
  },
50
53
  "pnpm": {
51
54
  "overrides": {
@@ -60,7 +63,10 @@
60
63
  },
61
64
  "devDependencies": {
62
65
  "@gorhom/bottom-sheet": "^5.0.0",
66
+ "@react-native-picker/picker": "2.11.4",
67
+ "@react-native-community/slider": "^4.5.5",
63
68
  "@types/react": "^19.1.0",
69
+ "@expo/vector-icons": "^15.1.1",
64
70
  "expo-haptics": "~15.0.8",
65
71
  "expo-linear-gradient": "~15.0.8",
66
72
  "react": "19.1.0",
@@ -14,6 +14,7 @@ import ReanimatedAnimated, {
14
14
  withTiming,
15
15
  Easing,
16
16
  } from 'react-native-reanimated'
17
+ import { Entypo } from '@expo/vector-icons'
17
18
  import * as Haptics from 'expo-haptics'
18
19
  import { useTheme } from '../../theme'
19
20
 
@@ -99,11 +100,9 @@ function AccordionItemComponent({
99
100
  touchSoundDisabled={true}
100
101
  >
101
102
  <Text style={[styles.triggerText, { color: colors.foreground }]}>{item.trigger}</Text>
102
- <ReanimatedAnimated.Text
103
- style={[styles.chevron, { color: colors.foreground }, rotationStyle]}
104
- >
105
-
106
- </ReanimatedAnimated.Text>
103
+ <ReanimatedAnimated.View style={[styles.chevron, rotationStyle]}>
104
+ <Entypo name="chevron-down" size={20} color={colors.foreground} />
105
+ </ReanimatedAnimated.View>
107
106
  </TouchableOpacity>
108
107
  </Animated.View>
109
108
 
@@ -162,7 +161,6 @@ const styles = StyleSheet.create({
162
161
  flex: 1,
163
162
  },
164
163
  chevron: {
165
- fontSize: 18,
166
164
  marginLeft: 8,
167
165
  },
168
166
  content: {
@@ -39,13 +39,13 @@ export function AlertBanner({ title, description, variant = 'default', icon, sty
39
39
  <View style={styles.icon}>{icon}</View>
40
40
  ) : (
41
41
  <View style={styles.icon}>
42
- <Text style={[styles.defaultIcon, { color: titleColor }]}>{defaultIcon}</Text>
42
+ <Text style={[styles.defaultIcon, { color: titleColor }]} allowFontScaling={true}>{defaultIcon}</Text>
43
43
  </View>
44
44
  )}
45
45
  <View style={styles.content}>
46
- {title ? <Text style={[styles.title, { color: titleColor }]}>{title}</Text> : null}
46
+ {title ? <Text style={[styles.title, { color: titleColor }]} allowFontScaling={true}>{title}</Text> : null}
47
47
  {description ? (
48
- <Text style={[styles.description, { color: descColor }]}>{description}</Text>
48
+ <Text style={[styles.description, { color: descColor }]} allowFontScaling={true}>{description}</Text>
49
49
  ) : null}
50
50
  </View>
51
51
  </View>
@@ -0,0 +1,85 @@
1
+ import React from 'react'
2
+ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
+ import { FontAwesome5, MaterialIcons, Entypo } from '@expo/vector-icons'
4
+ import { useTheme } from '../../theme'
5
+
6
+ export type AlertBannerVariant = 'default' | 'destructive' | 'success'
7
+
8
+ export interface AlertBannerProps {
9
+ title?: string
10
+ description?: string
11
+ variant?: AlertBannerVariant
12
+ icon?: React.ReactNode
13
+ style?: ViewStyle
14
+ }
15
+
16
+ export function AlertBanner({ title, description, variant = 'default', icon, style }: AlertBannerProps) {
17
+ const { colors } = useTheme()
18
+
19
+ const borderColor =
20
+ variant === 'destructive' ? colors.destructive
21
+ : variant === 'success' ? colors.success
22
+ : colors.border
23
+
24
+ const titleColor =
25
+ variant === 'destructive' ? colors.destructive
26
+ : variant === 'success' ? colors.success
27
+ : colors.foreground
28
+
29
+ const descColor =
30
+ variant === 'destructive' ? colors.destructive
31
+ : variant === 'success' ? colors.success
32
+ : colors.mutedForeground
33
+
34
+ const defaultIcon =
35
+ variant === 'success' ? (
36
+ <FontAwesome5 name="check-circle" size={18} color={titleColor} />
37
+ ) : variant === 'destructive' ? (
38
+ <MaterialIcons name="error-outline" size={20} color={titleColor} />
39
+ ) : (
40
+ <Entypo name="info-with-circle" size={18} color={titleColor} />
41
+ )
42
+
43
+ return (
44
+ <View style={[styles.container, { backgroundColor: colors.card, borderColor }, style]}>
45
+ <View style={styles.icon}>{icon ?? defaultIcon}</View>
46
+ <View style={styles.content}>
47
+ {title ? <Text style={[styles.title, { color: titleColor }]} allowFontScaling={true}>{title}</Text> : null}
48
+ {description ? (
49
+ <Text style={[styles.description, { color: descColor }]} allowFontScaling={true}>{description}</Text>
50
+ ) : null}
51
+ </View>
52
+ </View>
53
+ )
54
+ }
55
+
56
+ const styles = StyleSheet.create({
57
+ container: {
58
+ flexDirection: 'row',
59
+ borderWidth: 1,
60
+ borderRadius: 12,
61
+ padding: 16,
62
+ gap: 12,
63
+ shadowColor: '#000',
64
+ shadowOffset: { width: 0, height: 4 },
65
+ shadowOpacity: 0.06,
66
+ shadowRadius: 12,
67
+ elevation: 3,
68
+ },
69
+ icon: {
70
+ marginTop: 0,
71
+ },
72
+ content: {
73
+ flex: 1,
74
+ gap: 4,
75
+ },
76
+ title: {
77
+ fontSize: 14,
78
+ fontWeight: '500',
79
+ lineHeight: 20,
80
+ },
81
+ description: {
82
+ fontSize: 14,
83
+ lineHeight: 20,
84
+ },
85
+ })
@@ -1,2 +1,2 @@
1
- export { AlertBanner } from './Alert'
2
- export type { AlertBannerProps, AlertBannerVariant } from './Alert'
1
+ export { AlertBanner } from './AlertBanner'
2
+ export type { AlertBannerProps, AlertBannerVariant } from './AlertBanner'
@@ -52,6 +52,7 @@ export function Avatar({ src, fallback, size = 'md', style }: AvatarProps) {
52
52
  ) : (
53
53
  <Text
54
54
  style={[styles.fallback, { color: colors.mutedForeground, fontSize: fontSizeMap[size] }]}
55
+ allowFontScaling={true}
55
56
  >
56
57
  {fallback?.slice(0, 2).toUpperCase() ?? '?'}
57
58
  </Text>
@@ -1,16 +1,40 @@
1
1
  import React from 'react'
2
- import { View, Text, StyleSheet, ViewStyle } from 'react-native'
2
+ import { View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native'
3
3
  import { useTheme } from '../../theme'
4
4
 
5
5
  export type BadgeVariant = 'default' | 'secondary' | 'destructive' | 'outline'
6
+ export type BadgeSize = 'sm' | 'md' | 'lg'
6
7
 
7
8
  export interface BadgeProps {
8
- label: string
9
+ label?: string
10
+ /** Alternative to \`label\` — allows JSX children. */
11
+ children?: React.ReactNode
9
12
  variant?: BadgeVariant
13
+ size?: BadgeSize
14
+ /** Icon rendered before the label/children. */
15
+ icon?: React.ReactNode
10
16
  style?: ViewStyle
11
17
  }
12
18
 
13
- export function Badge({ label, variant = 'default', style }: BadgeProps) {
19
+ const sizePadding: Record<BadgeSize, ViewStyle> = {
20
+ sm: { paddingHorizontal: 8, paddingVertical: 2 },
21
+ md: { paddingHorizontal: 10, paddingVertical: 4 },
22
+ lg: { paddingHorizontal: 12, paddingVertical: 6 },
23
+ }
24
+
25
+ const sizeFontSize: Record<BadgeSize, TextStyle> = {
26
+ sm: { fontSize: 11 },
27
+ md: { fontSize: 13 },
28
+ lg: { fontSize: 15 },
29
+ }
30
+
31
+ const sizeIconGap: Record<BadgeSize, number> = {
32
+ sm: 4,
33
+ md: 6,
34
+ lg: 6,
35
+ }
36
+
37
+ export function Badge({ label, children, variant = 'default', size = 'md', icon, style }: BadgeProps) {
14
38
  const { colors } = useTheme()
15
39
 
16
40
  const containerStyle: ViewStyle = {
@@ -27,22 +51,34 @@ export function Badge({ label, variant = 'default', style }: BadgeProps) {
27
51
  outline: colors.foreground,
28
52
  }[variant]
29
53
 
54
+ const content = children ?? label
55
+
30
56
  return (
31
- <View style={[styles.container, containerStyle, style]}>
32
- <Text style={[styles.label, { color: textColor }]} allowFontScaling={true}>{label}</Text>
57
+ <View style={[styles.container, containerStyle, sizePadding[size], { gap: sizeIconGap[size] }, style]}>
58
+ {icon ? <View style={styles.iconContainer}>{icon}</View> : null}
59
+ {typeof content === 'string' ? (
60
+ <Text style={[styles.label, { color: textColor }, sizeFontSize[size]]} allowFontScaling={true}>
61
+ {content}
62
+ </Text>
63
+ ) : (
64
+ content
65
+ )}
33
66
  </View>
34
67
  )
35
68
  }
36
69
 
37
70
  const styles = StyleSheet.create({
38
71
  container: {
39
- borderRadius: 8,
40
- paddingHorizontal: 10,
41
- paddingVertical: 4,
72
+ borderRadius: 6,
42
73
  alignSelf: 'flex-start',
74
+ flexDirection: 'row',
75
+ alignItems: 'center',
76
+ },
77
+ iconContainer: {
78
+ justifyContent: 'center',
79
+ alignItems: 'center',
43
80
  },
44
81
  label: {
45
- fontSize: 13,
46
82
  fontWeight: '500',
47
83
  },
48
84
  })
@@ -38,9 +38,9 @@ export interface ButtonProps extends TouchableOpacityProps {
38
38
  }
39
39
 
40
40
  const containerSizeStyles: Record<ButtonSize, ViewStyle> = {
41
- sm: { paddingHorizontal: 20, paddingVertical: 12 },
42
- md: { paddingHorizontal: 24, paddingVertical: 16 },
43
- lg: { paddingHorizontal: 32, paddingVertical: 20 },
41
+ sm: { paddingHorizontal: 20, paddingVertical: 10 },
42
+ md: { paddingHorizontal: 24, paddingVertical: 14 },
43
+ lg: { paddingHorizontal: 32, paddingVertical: 18 },
44
44
  }
45
45
 
46
46
  const labelSizeStyles: Record<ButtonSize, TextStyle> = {
@@ -150,12 +150,12 @@ const styles = StyleSheet.create({
150
150
  width: '100%',
151
151
  },
152
152
  disabled: {
153
- opacity: 0.45,
153
+ opacity: 0.5,
154
154
  },
155
155
  label: {
156
156
  fontWeight: '600',
157
157
  },
158
158
  labelWithIcon: {
159
- marginHorizontal: 6,
159
+ marginHorizontal: 8,
160
160
  },
161
161
  })
@@ -1,9 +1,18 @@
1
- import React from 'react'
2
- import { View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native'
1
+ import React, { useRef } from 'react'
2
+ import { View, Text, TouchableOpacity, Animated, StyleSheet, ViewStyle, TextStyle, Platform } from 'react-native'
3
+ import * as Haptics from 'expo-haptics'
3
4
  import { useTheme } from '../../theme'
4
5
 
6
+ const nativeDriver = Platform.OS !== 'web'
7
+
8
+ export type CardVariant = 'elevated' | 'outlined' | 'filled'
9
+
5
10
  export interface CardProps {
6
11
  children: React.ReactNode
12
+ /** Visual style variant. `'elevated'` (default) has shadow, `'outlined'` has border only, `'filled'` uses accent background. */
13
+ variant?: CardVariant
14
+ /** Makes the card tappable. Adds press animation and haptic feedback. */
15
+ onPress?: () => void
7
16
  style?: ViewStyle
8
17
  }
9
18
 
@@ -32,15 +41,83 @@ export interface CardFooterProps {
32
41
  style?: ViewStyle
33
42
  }
34
43
 
35
- export function Card({ children, style }: CardProps) {
44
+ export function Card({ children, variant = 'elevated', onPress, style }: CardProps) {
36
45
  const { colors } = useTheme()
37
- return (
38
- <View
39
- style={[styles.card, { backgroundColor: colors.card, borderColor: colors.border }, style]}
40
- >
46
+ const scale = useRef(new Animated.Value(1)).current
47
+
48
+ const handlePressIn = () => {
49
+ if (!onPress) return
50
+ Animated.spring(scale, {
51
+ toValue: 0.98,
52
+ useNativeDriver: nativeDriver,
53
+ speed: 40,
54
+ bounciness: 0,
55
+ }).start()
56
+ }
57
+
58
+ const handlePressOut = () => {
59
+ if (!onPress) return
60
+ Animated.spring(scale, {
61
+ toValue: 1,
62
+ useNativeDriver: nativeDriver,
63
+ speed: 40,
64
+ bounciness: 4,
65
+ }).start()
66
+ }
67
+
68
+ const handlePress = () => {
69
+ if (!onPress) return
70
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
71
+ onPress()
72
+ }
73
+
74
+ const variantStyle: ViewStyle = {
75
+ elevated: {
76
+ backgroundColor: colors.card,
77
+ borderColor: colors.border,
78
+ shadowColor: '#000',
79
+ shadowOffset: { width: 0, height: 4 },
80
+ shadowOpacity: 0.06,
81
+ shadowRadius: 12,
82
+ elevation: 3,
83
+ },
84
+ outlined: {
85
+ backgroundColor: colors.card,
86
+ borderColor: colors.border,
87
+ shadowOpacity: 0,
88
+ elevation: 0,
89
+ },
90
+ filled: {
91
+ backgroundColor: colors.accent,
92
+ borderColor: colors.border,
93
+ shadowOpacity: 0,
94
+ elevation: 0,
95
+ },
96
+ }[variant]
97
+
98
+ const cardContent = (
99
+ <View style={[styles.card, variantStyle, style]}>
41
100
  {children}
42
101
  </View>
43
102
  )
103
+
104
+ if (onPress) {
105
+ return (
106
+ <Animated.View style={{ transform: [{ scale }] }}>
107
+ <TouchableOpacity
108
+ onPress={handlePress}
109
+ onPressIn={handlePressIn}
110
+ onPressOut={handlePressOut}
111
+ activeOpacity={1}
112
+ touchSoundDisabled={true}
113
+ >
114
+ {cardContent}
115
+ </TouchableOpacity>
116
+ </Animated.View>
117
+ )
118
+ }
119
+
120
+ return cardContent
44
121
  }
45
122
 
46
123
  export function CardHeader({ children, style }: CardHeaderProps) {
@@ -49,13 +126,13 @@ export function CardHeader({ children, style }: CardHeaderProps) {
49
126
 
50
127
  export function CardTitle({ children, style }: CardTitleProps) {
51
128
  const { colors } = useTheme()
52
- return <Text style={[styles.title, { color: colors.cardForeground }, style]}>{children}</Text>
129
+ return <Text style={[styles.title, { color: colors.cardForeground }, style]} allowFontScaling={true}>{children}</Text>
53
130
  }
54
131
 
55
132
  export function CardDescription({ children, style }: CardDescriptionProps) {
56
133
  const { colors } = useTheme()
57
134
  return (
58
- <Text style={[styles.description, { color: colors.mutedForeground }, style]}>{children}</Text>
135
+ <Text style={[styles.description, { color: colors.mutedForeground }, style]} allowFontScaling={true}>{children}</Text>
59
136
  )
60
137
  }
61
138
 
@@ -69,16 +146,11 @@ export function CardFooter({ children, style }: CardFooterProps) {
69
146
 
70
147
  const styles = StyleSheet.create({
71
148
  card: {
72
- borderRadius: 20,
149
+ borderRadius: 12,
73
150
  borderWidth: 1,
74
- shadowColor: '#000',
75
- shadowOffset: { width: 0, height: 1 },
76
- shadowOpacity: 0.05,
77
- shadowRadius: 2,
78
- elevation: 1,
79
151
  },
80
152
  header: {
81
- padding: 28,
153
+ padding: 24,
82
154
  paddingBottom: 0,
83
155
  gap: 8,
84
156
  },
@@ -92,10 +164,10 @@ const styles = StyleSheet.create({
92
164
  lineHeight: 22,
93
165
  },
94
166
  content: {
95
- padding: 28,
167
+ padding: 24,
96
168
  },
97
169
  footer: {
98
- padding: 28,
170
+ padding: 24,
99
171
  paddingTop: 0,
100
172
  flexDirection: 'row',
101
173
  alignItems: 'center',
@@ -77,16 +77,16 @@ const styles = StyleSheet.create({
77
77
  gap: 12,
78
78
  },
79
79
  box: {
80
- width: 28,
81
- height: 28,
80
+ width: 24,
81
+ height: 24,
82
82
  borderRadius: 8,
83
83
  borderWidth: 1.5,
84
84
  alignItems: 'center',
85
85
  justifyContent: 'center',
86
86
  },
87
87
  checkmark: {
88
- width: 15,
89
- height: 9,
88
+ width: 12,
89
+ height: 7,
90
90
  borderLeftWidth: 2,
91
91
  borderBottomWidth: 2,
92
92
  transform: [{ rotate: '-45deg' }, { translateY: -1 }],
@@ -28,8 +28,10 @@ export interface ChipOption {
28
28
 
29
29
  export interface ChipGroupProps {
30
30
  options: ChipOption[]
31
- value?: string | number
32
- onValueChange?: (value: string | number) => void
31
+ value?: string | number | (string | number)[]
32
+ onValueChange?: (value: string | number | (string | number)[]) => void
33
+ /** When true, allows selecting multiple chips. `value` and `onValueChange` will use arrays. */
34
+ multiSelect?: boolean
33
35
  style?: ViewStyle
34
36
  }
35
37
 
@@ -104,15 +106,44 @@ export function Chip({ label, selected = false, onPress, style }: ChipProps) {
104
106
  )
105
107
  }
106
108
 
107
- export function ChipGroup({ options, value, onValueChange, style }: ChipGroupProps) {
109
+ export function ChipGroup({ options, value, onValueChange, multiSelect = false, style }: ChipGroupProps) {
110
+ const handlePress = (optionValue: string | number) => {
111
+ if (!multiSelect) {
112
+ onValueChange?.(optionValue)
113
+ return
114
+ }
115
+
116
+ // Multiselect logic
117
+ const currentArray = Array.isArray(value) ? value : value ? [value] : []
118
+ const isSelected = currentArray.includes(optionValue)
119
+
120
+ let newArray: (string | number)[]
121
+ if (isSelected) {
122
+ // Remove from selection
123
+ newArray = currentArray.filter((v) => v !== optionValue)
124
+ } else {
125
+ // Add to selection
126
+ newArray = [...currentArray, optionValue]
127
+ }
128
+
129
+ onValueChange?.(newArray)
130
+ }
131
+
132
+ const isSelected = (optionValue: string | number): boolean => {
133
+ if (Array.isArray(value)) {
134
+ return value.includes(optionValue)
135
+ }
136
+ return optionValue === value
137
+ }
138
+
108
139
  return (
109
140
  <View style={[styles.group, style]}>
110
141
  {options.map((opt) => (
111
142
  <Chip
112
143
  key={opt.value}
113
144
  label={opt.label}
114
- selected={opt.value === value}
115
- onPress={() => onValueChange?.(opt.value)}
145
+ selected={isSelected(opt.value)}
146
+ onPress={() => handlePress(opt.value)}
116
147
  />
117
148
  ))}
118
149
  </View>
@@ -1,5 +1,5 @@
1
1
  import React from 'react'
2
- import { ViewStyle } from 'react-native'
2
+ import { ViewStyle, TextStyle } from 'react-native'
3
3
  import { Input } from '../Input'
4
4
 
5
5
  export interface CurrencyInputProps {
@@ -11,6 +11,8 @@ export interface CurrencyInputProps {
11
11
  prefix?: string
12
12
  /** Character used to separate groups of three digits. Defaults to `'.'`. */
13
13
  thousandsSeparator?: '.' | ','
14
+ /** Font size variant. `'large'` renders at 36pt, `'default'` at 17pt. */
15
+ size?: 'default' | 'large'
14
16
  label?: string
15
17
  /** Red helper text; also changes input border to destructive color. */
16
18
  error?: string
@@ -18,6 +20,7 @@ export interface CurrencyInputProps {
18
20
  placeholder?: string
19
21
  editable?: boolean
20
22
  containerStyle?: ViewStyle
23
+ style?: TextStyle
21
24
  }
22
25
 
23
26
  function formatCurrency(raw: string, separator: '.' | ','): string {
@@ -32,12 +35,14 @@ export function CurrencyInput({
32
35
  onChangeValue,
33
36
  prefix = '$',
34
37
  thousandsSeparator = '.',
38
+ size = 'default',
35
39
  label,
36
40
  error,
37
41
  hint,
38
42
  placeholder,
39
43
  editable,
40
44
  containerStyle,
45
+ style,
41
46
  }: CurrencyInputProps) {
42
47
  const handleChange = (text: string) => {
43
48
  const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text
@@ -49,6 +54,8 @@ export function CurrencyInput({
49
54
  onChangeValue?.(isNaN(raw) ? 0 : raw)
50
55
  }
51
56
 
57
+ const inputStyle: TextStyle = size === 'large' ? { fontSize: 36 } : {}
58
+
52
59
  return (
53
60
  <Input
54
61
  value={value}
@@ -60,6 +67,7 @@ export function CurrencyInput({
60
67
  placeholder={placeholder ?? `${prefix}0`}
61
68
  editable={editable}
62
69
  containerStyle={containerStyle}
70
+ style={[inputStyle, style]}
63
71
  />
64
72
  )
65
73
  }
@@ -39,11 +39,12 @@ export function EmptyState({ icon, title, description, action, size = 'default',
39
39
  <View style={styles.textWrapper}>
40
40
  <Text
41
41
  style={[styles.title, isCompact && styles.titleCompact, { color: colors.foreground }]}
42
+ allowFontScaling={true}
42
43
  >
43
44
  {title}
44
45
  </Text>
45
46
  {description && !isCompact ? (
46
- <Text style={[styles.description, { color: colors.mutedForeground }]}>{description}</Text>
47
+ <Text style={[styles.description, { color: colors.mutedForeground }]} allowFontScaling={true}>{description}</Text>
47
48
  ) : null}
48
49
  </View>
49
50
  {action && !isCompact ? <View style={styles.action}>{action}</View> : null}