@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.
- package/COMPONENTS.md +150 -43
- package/dist/index.d.mts +80 -44
- package/dist/index.d.ts +80 -44
- package/dist/index.js +627 -457
- package/dist/index.mjs +626 -457
- package/package.json +8 -2
- package/src/components/Accordion/Accordion.tsx +4 -6
- package/src/components/Alert/Alert.tsx +3 -3
- package/src/components/AlertBanner/AlertBanner.tsx +85 -0
- package/src/components/{Alert → AlertBanner}/index.ts +2 -2
- package/src/components/Avatar/Avatar.tsx +1 -0
- package/src/components/Badge/Badge.tsx +45 -9
- package/src/components/Button/Button.tsx +5 -5
- package/src/components/Card/Card.tsx +90 -18
- package/src/components/Checkbox/Checkbox.tsx +4 -4
- package/src/components/Chip/Chip.tsx +36 -5
- package/src/components/CurrencyInput/CurrencyInput.tsx +9 -1
- package/src/components/EmptyState/EmptyState.tsx +2 -1
- package/src/components/Input/Input.tsx +107 -26
- package/src/components/ListItem/ListItem.tsx +157 -21
- package/src/components/MonthPicker/MonthPicker.tsx +3 -6
- package/src/components/RadioGroup/RadioGroup.tsx +2 -2
- package/src/components/Select/Select.tsx +200 -132
- package/src/components/Slider/Slider.tsx +64 -100
- package/src/components/Switch/Switch.tsx +22 -20
- package/src/components/Textarea/Textarea.tsx +16 -7
- package/src/components/Toast/Toast.tsx +23 -18
- package/src/components/Toggle/Toggle.tsx +36 -49
- package/src/index.ts +3 -2
- package/src/theme/ThemeProvider.tsx +11 -8
- package/src/theme/colors.ts +19 -18
- package/src/theme/types.ts +2 -0
- package/src/components/CurrencyInputLarge/CurrencyInputLarge.tsx +0 -66
- package/src/components/CurrencyInputLarge/index.ts +0 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle } from 'react-native'
|
|
2
|
+
import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle, Platform } from 'react-native'
|
|
3
3
|
import { useTheme } from '../../theme'
|
|
4
4
|
|
|
5
|
+
const webInputResetStyle: any =
|
|
6
|
+
Platform.OS === 'web'
|
|
7
|
+
? { outlineStyle: 'none', outlineWidth: 0, outlineColor: 'transparent', boxShadow: 'none' }
|
|
8
|
+
: {}
|
|
9
|
+
|
|
5
10
|
export interface TextareaProps extends TextInputProps {
|
|
6
11
|
label?: string
|
|
7
12
|
/** Red helper text below the textarea; also changes border to `destructive` color. Takes priority over `hint`. */
|
|
@@ -38,11 +43,16 @@ export function Textarea({
|
|
|
38
43
|
style={[
|
|
39
44
|
styles.input,
|
|
40
45
|
{
|
|
41
|
-
borderColor: error
|
|
46
|
+
borderColor: error
|
|
47
|
+
? colors.destructive
|
|
48
|
+
: focused
|
|
49
|
+
? (colors.ring ?? colors.primary)
|
|
50
|
+
: colors.border,
|
|
42
51
|
color: colors.foreground,
|
|
43
52
|
backgroundColor: colors.background,
|
|
44
53
|
minHeight: rows * 30,
|
|
45
54
|
},
|
|
55
|
+
webInputResetStyle,
|
|
46
56
|
style,
|
|
47
57
|
]}
|
|
48
58
|
onFocus={(e) => {
|
|
@@ -69,18 +79,17 @@ export function Textarea({
|
|
|
69
79
|
|
|
70
80
|
const styles = StyleSheet.create({
|
|
71
81
|
container: {
|
|
72
|
-
gap:
|
|
82
|
+
gap: 8,
|
|
73
83
|
},
|
|
74
84
|
label: {
|
|
75
85
|
fontSize: 15,
|
|
76
86
|
fontWeight: '500',
|
|
77
|
-
marginBottom: 6,
|
|
78
87
|
},
|
|
79
88
|
input: {
|
|
80
89
|
borderWidth: 1.5,
|
|
81
|
-
borderRadius:
|
|
82
|
-
paddingHorizontal:
|
|
83
|
-
paddingVertical:
|
|
90
|
+
borderRadius: 8,
|
|
91
|
+
paddingHorizontal: 16,
|
|
92
|
+
paddingVertical: 14,
|
|
84
93
|
fontSize: 17,
|
|
85
94
|
},
|
|
86
95
|
helperText: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'
|
|
2
|
-
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, Platform } from 'react-native'
|
|
3
|
+
import { FontAwesome5, MaterialIcons, Entypo, AntDesign } from '@expo/vector-icons'
|
|
3
4
|
import Animated, {
|
|
4
5
|
useSharedValue,
|
|
5
6
|
useAnimatedStyle,
|
|
@@ -98,11 +99,16 @@ function ToastNotification({ item, onDismiss }: { item: ToastItem; onDismiss: ()
|
|
|
98
99
|
success: colors.successForeground,
|
|
99
100
|
}[item.variant ?? 'default']
|
|
100
101
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
const defaultIcon =
|
|
103
|
+
item.variant === 'success' ? (
|
|
104
|
+
<FontAwesome5 name="check-circle" size={22} color={textColor} />
|
|
105
|
+
) : item.variant === 'destructive' ? (
|
|
106
|
+
<MaterialIcons name="error-outline" size={24} color={textColor} />
|
|
107
|
+
) : (
|
|
108
|
+
<Entypo name="info-with-circle" size={22} color={textColor} />
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const leftIcon = item.icon ?? defaultIcon
|
|
106
112
|
|
|
107
113
|
return (
|
|
108
114
|
<GestureDetector gesture={panGesture}>
|
|
@@ -113,13 +119,13 @@ function ToastNotification({ item, onDismiss }: { item: ToastItem; onDismiss: ()
|
|
|
113
119
|
<Text style={[styles.toastTitle, { color: textColor }]}>{item.title}</Text>
|
|
114
120
|
) : null}
|
|
115
121
|
{item.description ? (
|
|
116
|
-
<Text style={[styles.toastDescription, { color: textColor, opacity: 0.85 }]}>
|
|
122
|
+
<Text style={[styles.toastDescription, { color: textColor, opacity: 0.85 }]}>
|
|
117
123
|
{item.description}
|
|
118
124
|
</Text>
|
|
119
125
|
) : null}
|
|
120
126
|
</View>
|
|
121
127
|
<TouchableOpacity onPress={onDismiss} style={styles.dismissButton} touchSoundDisabled={true}>
|
|
122
|
-
<
|
|
128
|
+
<AntDesign name="close-circle" size={18} color={textColor} />
|
|
123
129
|
</TouchableOpacity>
|
|
124
130
|
</Animated.View>
|
|
125
131
|
</GestureDetector>
|
|
@@ -158,7 +164,7 @@ export function ToastProvider({ children }: ToastProviderProps) {
|
|
|
158
164
|
return (
|
|
159
165
|
<ToastContext.Provider value={{ toast, dismiss }}>
|
|
160
166
|
{children}
|
|
161
|
-
<View style={[styles.container, { top: insets.top + 8 }]} pointerEvents="box-none">
|
|
167
|
+
<View style={[styles.container, Platform.OS === 'web' && styles.containerWeb, { top: insets.top + 8 }]} pointerEvents="box-none">
|
|
162
168
|
{toasts.map((item) => (
|
|
163
169
|
<ToastNotification key={item.id} item={item} onDismiss={() => dismiss(item.id)} />
|
|
164
170
|
))}
|
|
@@ -175,6 +181,12 @@ const styles = StyleSheet.create({
|
|
|
175
181
|
gap: 8,
|
|
176
182
|
zIndex: 9999,
|
|
177
183
|
},
|
|
184
|
+
containerWeb: {
|
|
185
|
+
left: undefined,
|
|
186
|
+
right: undefined,
|
|
187
|
+
alignSelf: 'center',
|
|
188
|
+
width: 400,
|
|
189
|
+
},
|
|
178
190
|
toast: {
|
|
179
191
|
flexDirection: 'row',
|
|
180
192
|
alignItems: 'center',
|
|
@@ -192,15 +204,11 @@ const styles = StyleSheet.create({
|
|
|
192
204
|
gap: 4,
|
|
193
205
|
},
|
|
194
206
|
leftIconContainer: {
|
|
195
|
-
width:
|
|
207
|
+
width: 40,
|
|
196
208
|
alignItems: 'center',
|
|
197
209
|
justifyContent: 'center',
|
|
198
210
|
marginRight: 8,
|
|
199
211
|
},
|
|
200
|
-
defaultIcon: {
|
|
201
|
-
fontSize: 22,
|
|
202
|
-
fontWeight: '700',
|
|
203
|
-
},
|
|
204
212
|
toastTitle: {
|
|
205
213
|
fontSize: 15,
|
|
206
214
|
fontWeight: '600',
|
|
@@ -209,10 +217,7 @@ const styles = StyleSheet.create({
|
|
|
209
217
|
fontSize: 14,
|
|
210
218
|
},
|
|
211
219
|
dismissButton: {
|
|
212
|
-
padding:
|
|
220
|
+
padding: 8,
|
|
213
221
|
marginLeft: 4,
|
|
214
222
|
},
|
|
215
|
-
dismissIcon: {
|
|
216
|
-
fontSize: 14,
|
|
217
|
-
},
|
|
218
223
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useRef, useEffect } from 'react'
|
|
2
|
-
import { TouchableOpacity, Animated,
|
|
2
|
+
import { TouchableOpacity, Animated, StyleSheet, TouchableOpacityProps, ViewStyle, View, Easing } from 'react-native'
|
|
3
|
+
import { FontAwesome5 } from '@expo/vector-icons'
|
|
3
4
|
import * as Haptics from 'expo-haptics'
|
|
4
5
|
import { useTheme } from '../../theme'
|
|
5
6
|
|
|
@@ -83,46 +84,46 @@ export function Toggle({
|
|
|
83
84
|
return prop
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
if (
|
|
87
|
+
if (pressed) {
|
|
88
|
+
const active = renderProp(activeIcon)
|
|
89
|
+
if (active) return <>{active}</>
|
|
90
|
+
return <FontAwesome5 name="check-circle" size={20} color={colors.primary} />
|
|
91
|
+
}
|
|
87
92
|
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
93
|
+
const custom = renderProp(icon)
|
|
94
|
+
if (custom) return <>{custom}</>
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
<Text style={[styles.checkMark, { color: colors.primary }]}>✓</Text>
|
|
94
|
-
</View>
|
|
95
|
-
)
|
|
96
|
+
// Default: empty circle to signal an action is available
|
|
97
|
+
return <FontAwesome5 name="circle" size={20} color={colors.mutedForeground} />
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
return (
|
|
99
|
-
<Animated.View style={{ transform: [{ scale }] }}>
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
<Animated.View style={[{ transform: [{ scale }] }, disabled && styles.disabled, style]}>
|
|
102
|
+
<TouchableOpacity
|
|
103
|
+
onPress={() => {
|
|
104
|
+
Haptics.selectionAsync()
|
|
105
|
+
onPressedChange?.(!pressed)
|
|
106
|
+
}}
|
|
107
|
+
onPressIn={handlePressIn}
|
|
108
|
+
onPressOut={handlePressOut}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
activeOpacity={1}
|
|
111
|
+
touchSoundDisabled={true}
|
|
112
|
+
{...props}
|
|
108
113
|
>
|
|
109
|
-
<
|
|
110
|
-
style={
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
onPressIn={handlePressIn}
|
|
116
|
-
onPressOut={handlePressOut}
|
|
117
|
-
disabled={disabled}
|
|
118
|
-
activeOpacity={1}
|
|
119
|
-
touchSoundDisabled={true}
|
|
120
|
-
{...props}
|
|
114
|
+
<Animated.View
|
|
115
|
+
style={[
|
|
116
|
+
styles.base,
|
|
117
|
+
sizeStyles[size],
|
|
118
|
+
{ borderColor, backgroundColor, borderWidth: 2 },
|
|
119
|
+
]}
|
|
121
120
|
>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
<View style={styles.inner}>
|
|
122
|
+
<LeftIcon />
|
|
123
|
+
{label ? <Animated.Text style={[styles.label, { color: textColor }]}>{label}</Animated.Text> : null}
|
|
124
|
+
</View>
|
|
125
|
+
</Animated.View>
|
|
126
|
+
</TouchableOpacity>
|
|
126
127
|
</Animated.View>
|
|
127
128
|
)
|
|
128
129
|
}
|
|
@@ -130,14 +131,12 @@ export function Toggle({
|
|
|
130
131
|
const styles = StyleSheet.create({
|
|
131
132
|
base: {
|
|
132
133
|
borderRadius: 8,
|
|
133
|
-
overflow: 'hidden',
|
|
134
134
|
},
|
|
135
|
-
|
|
135
|
+
inner: {
|
|
136
136
|
flexDirection: 'row',
|
|
137
137
|
alignItems: 'center',
|
|
138
138
|
justifyContent: 'center',
|
|
139
139
|
gap: 8,
|
|
140
|
-
flex: 1,
|
|
141
140
|
},
|
|
142
141
|
disabled: {
|
|
143
142
|
opacity: 0.45,
|
|
@@ -146,16 +145,4 @@ const styles = StyleSheet.create({
|
|
|
146
145
|
fontSize: 14,
|
|
147
146
|
fontWeight: '500',
|
|
148
147
|
},
|
|
149
|
-
checkContainer: {
|
|
150
|
-
width: 24,
|
|
151
|
-
height: 24,
|
|
152
|
-
borderRadius: 12,
|
|
153
|
-
borderWidth: 2,
|
|
154
|
-
alignItems: 'center',
|
|
155
|
-
justifyContent: 'center',
|
|
156
|
-
},
|
|
157
|
-
checkMark: {
|
|
158
|
-
fontSize: 14,
|
|
159
|
-
fontWeight: '700',
|
|
160
|
-
},
|
|
161
148
|
})
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export * from './components/Separator'
|
|
|
13
13
|
export * from './components/Spinner'
|
|
14
14
|
export * from './components/Skeleton'
|
|
15
15
|
export * from './components/Avatar'
|
|
16
|
-
export * from './components/
|
|
16
|
+
export * from './components/AlertBanner'
|
|
17
17
|
export * from './components/Progress'
|
|
18
18
|
export * from './components/EmptyState'
|
|
19
19
|
export * from './components/Textarea'
|
|
@@ -29,7 +29,8 @@ export * from './components/Select'
|
|
|
29
29
|
export * from './components/Toast'
|
|
30
30
|
export * from './components/CurrencyInput'
|
|
31
31
|
export * from './components/CurrencyDisplay'
|
|
32
|
-
|
|
32
|
+
// CurrencyInputLarge is deprecated — use <CurrencyInput size="large" /> instead
|
|
33
|
+
export { CurrencyInput as CurrencyInputLarge } from './components/CurrencyInput'
|
|
33
34
|
export * from './components/ListItem'
|
|
34
35
|
export * from './components/Chip'
|
|
35
36
|
export * from './components/ConfirmDialog'
|
|
@@ -11,11 +11,10 @@ const ThemeContext = createContext<ThemeContextValue>({
|
|
|
11
11
|
export interface ThemeProviderProps {
|
|
12
12
|
children: React.ReactNode
|
|
13
13
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* dark: { primary: '#818cf8', primaryForeground: '#fff' } }
|
|
14
|
+
* Optional full-palette overrides per scheme. Supply a partial or full `ThemeColors` object
|
|
15
|
+
* for `light` and/or `dark` to override the defaults.
|
|
16
|
+
* @example
|
|
17
|
+
* { light: { primary: '#6366f1', card: '#fff' }, dark: { primary: '#818cf8' } }
|
|
19
18
|
*/
|
|
20
19
|
theme?: Theme
|
|
21
20
|
/**
|
|
@@ -31,8 +30,8 @@ export function ThemeProvider({ children, theme, colorScheme = 'system' }: Theme
|
|
|
31
30
|
|
|
32
31
|
const colors = useMemo<ThemeColors>(() => {
|
|
33
32
|
const base = resolvedScheme === 'dark' ? defaultDark : defaultLight
|
|
34
|
-
const
|
|
35
|
-
return { ...base, ...
|
|
33
|
+
const override = resolvedScheme === 'dark' ? theme?.dark : theme?.light
|
|
34
|
+
return override ? { ...base, ...override } : base
|
|
36
35
|
}, [resolvedScheme, theme])
|
|
37
36
|
|
|
38
37
|
return (
|
|
@@ -43,5 +42,9 @@ export function ThemeProvider({ children, theme, colorScheme = 'system' }: Theme
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
export function useTheme(): ThemeContextValue {
|
|
46
|
-
|
|
45
|
+
const context = useContext(ThemeContext)
|
|
46
|
+
if (!context) {
|
|
47
|
+
throw new Error('useTheme must be used within a ThemeProvider')
|
|
48
|
+
}
|
|
49
|
+
return context
|
|
47
50
|
}
|
package/src/theme/colors.ts
CHANGED
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
import { ThemeColors } from './types'
|
|
2
2
|
|
|
3
|
+
// Full, explicit theme palettes. No derivation — palettes are direct and fully customizable.
|
|
3
4
|
export const defaultLight: ThemeColors = {
|
|
4
5
|
background: '#ffffff',
|
|
5
6
|
foreground: '#171717',
|
|
6
7
|
card: '#ffffff',
|
|
7
8
|
cardForeground: '#171717',
|
|
8
9
|
primary: '#1a1a1a',
|
|
9
|
-
primaryForeground: '#
|
|
10
|
-
secondary: '#
|
|
11
|
-
secondaryForeground: '#
|
|
12
|
-
muted: '#
|
|
13
|
-
mutedForeground: '#
|
|
14
|
-
accent: '#
|
|
15
|
-
accentForeground: '#
|
|
10
|
+
primaryForeground: '#ffffff',
|
|
11
|
+
secondary: '#f1f1f1',
|
|
12
|
+
secondaryForeground: '#171717',
|
|
13
|
+
muted: '#f1f1f1',
|
|
14
|
+
mutedForeground: '#a2a2a2',
|
|
15
|
+
accent: '#e4e4e4',
|
|
16
|
+
accentForeground: '#171717',
|
|
16
17
|
destructive: '#ef4444',
|
|
17
|
-
destructiveForeground: '#
|
|
18
|
+
destructiveForeground: '#1a1a1a',
|
|
18
19
|
border: '#e5e5e5',
|
|
19
20
|
input: '#e5e5e5',
|
|
20
|
-
ring: '#
|
|
21
|
+
ring: '#1a1a1a',
|
|
21
22
|
success: '#16a34a',
|
|
22
|
-
successForeground: '#
|
|
23
|
+
successForeground: '#1a1a1a',
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const defaultDark: ThemeColors = {
|
|
26
27
|
background: '#171717',
|
|
27
28
|
foreground: '#fafafa',
|
|
28
|
-
card: '#
|
|
29
|
+
card: '#222222',
|
|
29
30
|
cardForeground: '#fafafa',
|
|
30
31
|
primary: '#fafafa',
|
|
31
32
|
primaryForeground: '#1a1a1a',
|
|
32
|
-
secondary: '#
|
|
33
|
+
secondary: '#323232',
|
|
33
34
|
secondaryForeground: '#fafafa',
|
|
34
|
-
muted: '#
|
|
35
|
-
mutedForeground: '#
|
|
36
|
-
accent: '#
|
|
35
|
+
muted: '#323232',
|
|
36
|
+
mutedForeground: '#888888',
|
|
37
|
+
accent: '#323232',
|
|
37
38
|
accentForeground: '#fafafa',
|
|
38
39
|
destructive: '#dc2626',
|
|
39
|
-
destructiveForeground: '#
|
|
40
|
+
destructiveForeground: '#1a1a1a',
|
|
40
41
|
border: '#2a2a2a',
|
|
41
42
|
input: '#2a2a2a',
|
|
42
|
-
ring: '#
|
|
43
|
+
ring: '#fafafa',
|
|
43
44
|
success: '#22c55e',
|
|
44
|
-
successForeground: '#
|
|
45
|
+
successForeground: '#1a1a1a',
|
|
45
46
|
}
|
package/src/theme/types.ts
CHANGED
|
@@ -20,6 +20,8 @@ export type ThemeColors = {
|
|
|
20
20
|
successForeground: string
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// Theme overrides: consumers may supply partial or full `ThemeColors` objects
|
|
24
|
+
// for `theme.light` / `theme.dark` to override only the tokens they want.
|
|
23
25
|
export type Theme = {
|
|
24
26
|
light?: Partial<ThemeColors>
|
|
25
27
|
dark?: Partial<ThemeColors>
|
|
@@ -1,66 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './CurrencyInputLarge'
|