@retray-dev/ui-kit 2.3.0 → 2.5.1
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 +80 -26
- package/README.md +3 -5
- package/dist/index.d.mts +35 -3
- package/dist/index.d.ts +35 -3
- package/dist/index.js +204 -143
- package/dist/index.mjs +204 -143
- package/package.json +4 -2
- package/src/components/Accordion/Accordion.tsx +4 -6
- package/src/components/AlertBanner/AlertBanner.tsx +16 -14
- package/src/components/Badge/Badge.tsx +1 -1
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Card/Card.tsx +8 -13
- package/src/components/Checkbox/Checkbox.tsx +4 -4
- package/src/components/Input/Input.tsx +6 -6
- 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 +17 -13
- package/src/components/Textarea/Textarea.tsx +4 -5
- package/src/components/Toast/Toast.tsx +23 -18
- package/src/components/Toggle/Toggle.tsx +29 -49
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retray-dev/ui-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Personal UI Kit for React Native / Expo",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"react-native-worklets": ">=0.8.0",
|
|
48
48
|
"react-native-safe-area-context": ">=4.0.0",
|
|
49
49
|
"@react-native-picker/picker": ">=2.0.0",
|
|
50
|
-
"@react-native-community/slider": ">=4.0.0"
|
|
50
|
+
"@react-native-community/slider": ">=4.0.0",
|
|
51
|
+
"@expo/vector-icons": ">=14.0.0"
|
|
51
52
|
},
|
|
52
53
|
"pnpm": {
|
|
53
54
|
"overrides": {
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"@react-native-picker/picker": "2.11.4",
|
|
66
67
|
"@react-native-community/slider": "^4.5.5",
|
|
67
68
|
"@types/react": "^19.1.0",
|
|
69
|
+
"@expo/vector-icons": "^15.1.1",
|
|
68
70
|
"expo-haptics": "~15.0.8",
|
|
69
71
|
"expo-linear-gradient": "~15.0.8",
|
|
70
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.
|
|
103
|
-
|
|
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: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { FontAwesome5, MaterialIcons, Entypo } from '@expo/vector-icons'
|
|
3
4
|
import { useTheme } from '../../theme'
|
|
4
5
|
|
|
5
6
|
export type AlertBannerVariant = 'default' | 'destructive' | 'success'
|
|
@@ -31,17 +32,17 @@ export function AlertBanner({ title, description, variant = 'default', icon, sty
|
|
|
31
32
|
: colors.mutedForeground
|
|
32
33
|
|
|
33
34
|
const defaultIcon =
|
|
34
|
-
variant === '
|
|
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
|
+
)
|
|
35
42
|
|
|
36
43
|
return (
|
|
37
44
|
<View style={[styles.container, { backgroundColor: colors.card, borderColor }, style]}>
|
|
38
|
-
{icon
|
|
39
|
-
<View style={styles.icon}>{icon}</View>
|
|
40
|
-
) : (
|
|
41
|
-
<View style={styles.icon}>
|
|
42
|
-
<Text style={[styles.defaultIcon, { color: titleColor }]} allowFontScaling={true}>{defaultIcon}</Text>
|
|
43
|
-
</View>
|
|
44
|
-
)}
|
|
45
|
+
<View style={styles.icon}>{icon ?? defaultIcon}</View>
|
|
45
46
|
<View style={styles.content}>
|
|
46
47
|
{title ? <Text style={[styles.title, { color: titleColor }]} allowFontScaling={true}>{title}</Text> : null}
|
|
47
48
|
{description ? (
|
|
@@ -56,12 +57,17 @@ const styles = StyleSheet.create({
|
|
|
56
57
|
container: {
|
|
57
58
|
flexDirection: 'row',
|
|
58
59
|
borderWidth: 1,
|
|
59
|
-
borderRadius:
|
|
60
|
+
borderRadius: 12,
|
|
60
61
|
padding: 16,
|
|
61
62
|
gap: 12,
|
|
63
|
+
shadowColor: '#000',
|
|
64
|
+
shadowOffset: { width: 0, height: 4 },
|
|
65
|
+
shadowOpacity: 0.06,
|
|
66
|
+
shadowRadius: 12,
|
|
67
|
+
elevation: 3,
|
|
62
68
|
},
|
|
63
69
|
icon: {
|
|
64
|
-
marginTop:
|
|
70
|
+
marginTop: 0,
|
|
65
71
|
},
|
|
66
72
|
content: {
|
|
67
73
|
flex: 1,
|
|
@@ -76,8 +82,4 @@ const styles = StyleSheet.create({
|
|
|
76
82
|
fontSize: 14,
|
|
77
83
|
lineHeight: 20,
|
|
78
84
|
},
|
|
79
|
-
defaultIcon: {
|
|
80
|
-
fontSize: 18,
|
|
81
|
-
fontWeight: '700',
|
|
82
|
-
},
|
|
83
85
|
})
|
|
@@ -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:
|
|
42
|
-
md: { paddingHorizontal: 24, paddingVertical:
|
|
43
|
-
lg: { paddingHorizontal: 32, paddingVertical:
|
|
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> = {
|
|
@@ -156,6 +156,6 @@ const styles = StyleSheet.create({
|
|
|
156
156
|
fontWeight: '600',
|
|
157
157
|
},
|
|
158
158
|
labelWithIcon: {
|
|
159
|
-
marginHorizontal:
|
|
159
|
+
marginHorizontal: 8,
|
|
160
160
|
},
|
|
161
161
|
})
|
|
@@ -76,10 +76,10 @@ export function Card({ children, variant = 'elevated', onPress, style }: CardPro
|
|
|
76
76
|
backgroundColor: colors.card,
|
|
77
77
|
borderColor: colors.border,
|
|
78
78
|
shadowColor: '#000',
|
|
79
|
-
shadowOffset: { width: 0, height:
|
|
80
|
-
shadowOpacity: 0.
|
|
81
|
-
shadowRadius:
|
|
82
|
-
elevation:
|
|
79
|
+
shadowOffset: { width: 0, height: 4 },
|
|
80
|
+
shadowOpacity: 0.06,
|
|
81
|
+
shadowRadius: 12,
|
|
82
|
+
elevation: 3,
|
|
83
83
|
},
|
|
84
84
|
outlined: {
|
|
85
85
|
backgroundColor: colors.card,
|
|
@@ -146,16 +146,11 @@ export function CardFooter({ children, style }: CardFooterProps) {
|
|
|
146
146
|
|
|
147
147
|
const styles = StyleSheet.create({
|
|
148
148
|
card: {
|
|
149
|
-
borderRadius:
|
|
149
|
+
borderRadius: 12,
|
|
150
150
|
borderWidth: 1,
|
|
151
|
-
shadowColor: '#000',
|
|
152
|
-
shadowOffset: { width: 0, height: 1 },
|
|
153
|
-
shadowOpacity: 0.05,
|
|
154
|
-
shadowRadius: 2,
|
|
155
|
-
elevation: 1,
|
|
156
151
|
},
|
|
157
152
|
header: {
|
|
158
|
-
padding:
|
|
153
|
+
padding: 24,
|
|
159
154
|
paddingBottom: 0,
|
|
160
155
|
gap: 8,
|
|
161
156
|
},
|
|
@@ -169,10 +164,10 @@ const styles = StyleSheet.create({
|
|
|
169
164
|
lineHeight: 22,
|
|
170
165
|
},
|
|
171
166
|
content: {
|
|
172
|
-
padding:
|
|
167
|
+
padding: 24,
|
|
173
168
|
},
|
|
174
169
|
footer: {
|
|
175
|
-
padding:
|
|
170
|
+
padding: 24,
|
|
176
171
|
paddingTop: 0,
|
|
177
172
|
flexDirection: 'row',
|
|
178
173
|
alignItems: 'center',
|
|
@@ -77,16 +77,16 @@ const styles = StyleSheet.create({
|
|
|
77
77
|
gap: 12,
|
|
78
78
|
},
|
|
79
79
|
box: {
|
|
80
|
-
width:
|
|
81
|
-
height:
|
|
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:
|
|
89
|
-
height:
|
|
88
|
+
width: 12,
|
|
89
|
+
height: 7,
|
|
90
90
|
borderLeftWidth: 2,
|
|
91
91
|
borderBottomWidth: 2,
|
|
92
92
|
transform: [{ rotate: '-45deg' }, { translateY: -1 }],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle, TextStyle, TouchableOpacity, Platform } from 'react-native'
|
|
3
|
+
import { AntDesign } from '@expo/vector-icons'
|
|
3
4
|
import { useTheme } from '../../theme'
|
|
4
5
|
|
|
5
6
|
const webInputResetStyle: any =
|
|
@@ -38,7 +39,7 @@ export function Input({ label, error, hint, prefix, suffix, prefixStyle, suffixS
|
|
|
38
39
|
// If type is password and no suffix is provided, add the toggle button
|
|
39
40
|
const effectiveSuffix = isPassword && !suffix ? (
|
|
40
41
|
<TouchableOpacity onPress={() => setShowPassword(!showPassword)} style={styles.passwordToggle} activeOpacity={0.6}>
|
|
41
|
-
<
|
|
42
|
+
<AntDesign name={showPassword ? 'eye' : 'eye-invisible'} size={20} color={colors.mutedForeground} />
|
|
42
43
|
</TouchableOpacity>
|
|
43
44
|
) : suffix
|
|
44
45
|
|
|
@@ -111,20 +112,19 @@ export function Input({ label, error, hint, prefix, suffix, prefixStyle, suffixS
|
|
|
111
112
|
|
|
112
113
|
const styles = StyleSheet.create({
|
|
113
114
|
container: {
|
|
114
|
-
gap:
|
|
115
|
+
gap: 8,
|
|
115
116
|
},
|
|
116
117
|
label: {
|
|
117
118
|
fontSize: 15,
|
|
118
119
|
fontWeight: '500',
|
|
119
|
-
marginBottom: 6,
|
|
120
120
|
},
|
|
121
121
|
inputWrapper: {
|
|
122
122
|
flexDirection: 'row',
|
|
123
123
|
alignItems: 'center',
|
|
124
124
|
borderWidth: 1.5,
|
|
125
|
-
borderRadius:
|
|
126
|
-
paddingHorizontal:
|
|
127
|
-
paddingVertical:
|
|
125
|
+
borderRadius: 8,
|
|
126
|
+
paddingHorizontal: 16,
|
|
127
|
+
paddingVertical: 14,
|
|
128
128
|
},
|
|
129
129
|
input: {
|
|
130
130
|
flex: 1,
|
|
@@ -6,24 +6,81 @@ import {
|
|
|
6
6
|
Text,
|
|
7
7
|
StyleSheet,
|
|
8
8
|
ViewStyle,
|
|
9
|
+
TextStyle,
|
|
9
10
|
Platform,
|
|
10
11
|
} from 'react-native'
|
|
12
|
+
import { Entypo } from '@expo/vector-icons'
|
|
11
13
|
import * as Haptics from 'expo-haptics'
|
|
12
14
|
import { useTheme } from '../../theme'
|
|
13
15
|
|
|
14
16
|
const nativeDriver = Platform.OS !== 'web'
|
|
15
17
|
|
|
18
|
+
export type ListItemVariant = 'plain' | 'card'
|
|
19
|
+
|
|
16
20
|
export interface ListItemProps {
|
|
21
|
+
/**
|
|
22
|
+
* Arbitrary content rendered on the left (avatar, icon, image, etc.).
|
|
23
|
+
* Rendered inside a 44×44 aligned container.
|
|
24
|
+
*/
|
|
25
|
+
leftRender?: React.ReactNode
|
|
26
|
+
/**
|
|
27
|
+
* Arbitrary content rendered on the right (badge, price, chevron, switch, etc.).
|
|
28
|
+
* Replaces the old `trailing` prop (still accepted as an alias).
|
|
29
|
+
*/
|
|
30
|
+
rightRender?: React.ReactNode | string
|
|
31
|
+
/** @deprecated Use `rightRender` instead. */
|
|
32
|
+
trailing?: React.ReactNode | string
|
|
33
|
+
/** @deprecated Use `leftRender` instead. */
|
|
17
34
|
icon?: React.ReactNode
|
|
35
|
+
|
|
18
36
|
title: string
|
|
37
|
+
/** Secondary line below the title. */
|
|
19
38
|
subtitle?: string
|
|
20
|
-
|
|
39
|
+
/** Tertiary / caption line below the subtitle. */
|
|
40
|
+
caption?: string
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* - `plain` (default): no background, no border — designed to sit inside a parent surface (Card, list wrapper, etc.)
|
|
44
|
+
* - `card`: standalone surface with background, border and shadow.
|
|
45
|
+
*/
|
|
46
|
+
variant?: ListItemVariant
|
|
47
|
+
|
|
48
|
+
/** Show a right-pointing chevron on the far right. Ignored when `rightRender` / `trailing` is set. */
|
|
49
|
+
showChevron?: boolean
|
|
50
|
+
|
|
51
|
+
/** Visual separator line at the bottom of the item. Useful when rendering multiple plain items in a list. */
|
|
52
|
+
showSeparator?: boolean
|
|
53
|
+
|
|
21
54
|
onPress?: () => void
|
|
22
55
|
disabled?: boolean
|
|
56
|
+
/** Style applied to the outer container. */
|
|
23
57
|
style?: ViewStyle
|
|
58
|
+
/** Style applied to the title Text. */
|
|
59
|
+
titleStyle?: TextStyle
|
|
60
|
+
/** Style applied to the subtitle Text. */
|
|
61
|
+
subtitleStyle?: TextStyle
|
|
62
|
+
/** Style applied to the caption Text. */
|
|
63
|
+
captionStyle?: TextStyle
|
|
24
64
|
}
|
|
25
65
|
|
|
26
|
-
export function ListItem({
|
|
66
|
+
export function ListItem({
|
|
67
|
+
leftRender,
|
|
68
|
+
rightRender,
|
|
69
|
+
trailing,
|
|
70
|
+
icon,
|
|
71
|
+
title,
|
|
72
|
+
subtitle,
|
|
73
|
+
caption,
|
|
74
|
+
variant = 'plain',
|
|
75
|
+
showChevron = false,
|
|
76
|
+
showSeparator = false,
|
|
77
|
+
onPress,
|
|
78
|
+
disabled,
|
|
79
|
+
style,
|
|
80
|
+
titleStyle,
|
|
81
|
+
subtitleStyle,
|
|
82
|
+
captionStyle,
|
|
83
|
+
}: ListItemProps) {
|
|
27
84
|
const { colors } = useTheme()
|
|
28
85
|
const scale = useRef(new Animated.Value(1)).current
|
|
29
86
|
|
|
@@ -51,10 +108,29 @@ export function ListItem({ icon, title, subtitle, trailing, onPress, disabled, s
|
|
|
51
108
|
onPress?.()
|
|
52
109
|
}
|
|
53
110
|
|
|
111
|
+
// Support legacy props
|
|
112
|
+
const effectiveLeft = leftRender ?? icon
|
|
113
|
+
const effectiveRight = rightRender ?? trailing
|
|
114
|
+
|
|
115
|
+
const cardStyle: ViewStyle =
|
|
116
|
+
variant === 'card'
|
|
117
|
+
? {
|
|
118
|
+
backgroundColor: colors.card,
|
|
119
|
+
borderRadius: 12,
|
|
120
|
+
borderWidth: 1,
|
|
121
|
+
borderColor: colors.border,
|
|
122
|
+
shadowColor: '#000',
|
|
123
|
+
shadowOffset: { width: 0, height: 2 },
|
|
124
|
+
shadowOpacity: 0.06,
|
|
125
|
+
shadowRadius: 6,
|
|
126
|
+
elevation: 2,
|
|
127
|
+
}
|
|
128
|
+
: {}
|
|
129
|
+
|
|
54
130
|
return (
|
|
55
131
|
<Animated.View style={[{ transform: [{ scale }] }, disabled && styles.disabled]}>
|
|
56
132
|
<TouchableOpacity
|
|
57
|
-
style={[styles.container, style]}
|
|
133
|
+
style={[styles.container, cardStyle, style]}
|
|
58
134
|
onPress={onPress ? handlePress : undefined}
|
|
59
135
|
onPressIn={handlePressIn}
|
|
60
136
|
onPressOut={handlePressOut}
|
|
@@ -62,27 +138,64 @@ export function ListItem({ icon, title, subtitle, trailing, onPress, disabled, s
|
|
|
62
138
|
activeOpacity={1}
|
|
63
139
|
touchSoundDisabled={true}
|
|
64
140
|
>
|
|
65
|
-
{
|
|
141
|
+
{effectiveLeft ? (
|
|
142
|
+
<View style={styles.leftContainer}>{effectiveLeft}</View>
|
|
143
|
+
) : null}
|
|
144
|
+
|
|
66
145
|
<View style={styles.content}>
|
|
67
|
-
<Text
|
|
146
|
+
<Text
|
|
147
|
+
style={[styles.title, { color: colors.foreground }, titleStyle]}
|
|
148
|
+
numberOfLines={2}
|
|
149
|
+
allowFontScaling={true}
|
|
150
|
+
>
|
|
68
151
|
{title}
|
|
69
152
|
</Text>
|
|
70
153
|
{subtitle ? (
|
|
71
|
-
<Text
|
|
154
|
+
<Text
|
|
155
|
+
style={[styles.subtitle, { color: colors.mutedForeground }, subtitleStyle]}
|
|
156
|
+
numberOfLines={2}
|
|
157
|
+
allowFontScaling={true}
|
|
158
|
+
>
|
|
72
159
|
{subtitle}
|
|
73
160
|
</Text>
|
|
74
161
|
) : null}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{
|
|
162
|
+
{caption ? (
|
|
163
|
+
<Text
|
|
164
|
+
style={[styles.caption, { color: colors.mutedForeground }, captionStyle]}
|
|
165
|
+
numberOfLines={1}
|
|
166
|
+
allowFontScaling={true}
|
|
167
|
+
>
|
|
168
|
+
{caption}
|
|
80
169
|
</Text>
|
|
81
|
-
) :
|
|
82
|
-
|
|
83
|
-
|
|
170
|
+
) : null}
|
|
171
|
+
</View>
|
|
172
|
+
|
|
173
|
+
{effectiveRight !== undefined ? (
|
|
174
|
+
<View style={styles.rightContainer}>
|
|
175
|
+
{typeof effectiveRight === 'string' ? (
|
|
176
|
+
<Text
|
|
177
|
+
style={[styles.rightText, { color: colors.mutedForeground }]}
|
|
178
|
+
allowFontScaling={true}
|
|
179
|
+
>
|
|
180
|
+
{effectiveRight}
|
|
181
|
+
</Text>
|
|
182
|
+
) : (
|
|
183
|
+
effectiveRight
|
|
184
|
+
)}
|
|
185
|
+
</View>
|
|
186
|
+
) : showChevron ? (
|
|
187
|
+
<Entypo name="chevron-with-circle-right" size={20} color={colors.mutedForeground} />
|
|
84
188
|
) : null}
|
|
85
189
|
</TouchableOpacity>
|
|
190
|
+
|
|
191
|
+
{showSeparator ? (
|
|
192
|
+
<View
|
|
193
|
+
style={[
|
|
194
|
+
styles.separator,
|
|
195
|
+
{ backgroundColor: colors.border, marginLeft: effectiveLeft ? 16 + 44 + 12 : 16 },
|
|
196
|
+
]}
|
|
197
|
+
/>
|
|
198
|
+
) : null}
|
|
86
199
|
</Animated.View>
|
|
87
200
|
)
|
|
88
201
|
}
|
|
@@ -95,26 +208,49 @@ const styles = StyleSheet.create({
|
|
|
95
208
|
paddingVertical: 14,
|
|
96
209
|
gap: 12,
|
|
97
210
|
},
|
|
98
|
-
|
|
211
|
+
leftContainer: {
|
|
212
|
+
width: 44,
|
|
213
|
+
height: 44,
|
|
99
214
|
alignItems: 'center',
|
|
100
215
|
justifyContent: 'center',
|
|
216
|
+
flexShrink: 0,
|
|
101
217
|
},
|
|
102
218
|
content: {
|
|
103
219
|
flex: 1,
|
|
104
|
-
gap:
|
|
220
|
+
gap: 4,
|
|
105
221
|
},
|
|
106
222
|
title: {
|
|
107
|
-
fontSize:
|
|
223
|
+
fontSize: 17,
|
|
108
224
|
fontWeight: '500',
|
|
109
|
-
lineHeight:
|
|
225
|
+
lineHeight: 24,
|
|
110
226
|
},
|
|
111
227
|
subtitle: {
|
|
112
|
-
fontSize:
|
|
113
|
-
|
|
228
|
+
fontSize: 14,
|
|
229
|
+
fontWeight: '400',
|
|
230
|
+
lineHeight: 20,
|
|
114
231
|
},
|
|
115
|
-
|
|
232
|
+
caption: {
|
|
233
|
+
fontSize: 12,
|
|
234
|
+
fontWeight: '400',
|
|
235
|
+
lineHeight: 16,
|
|
236
|
+
opacity: 0.7,
|
|
237
|
+
},
|
|
238
|
+
rightContainer: {
|
|
239
|
+
alignItems: 'flex-end',
|
|
240
|
+
justifyContent: 'center',
|
|
241
|
+
flexShrink: 0,
|
|
242
|
+
maxWidth: 160,
|
|
243
|
+
},
|
|
244
|
+
rightText: {
|
|
116
245
|
fontSize: 15,
|
|
117
246
|
},
|
|
247
|
+
chevron: {
|
|
248
|
+
marginLeft: 4,
|
|
249
|
+
},
|
|
250
|
+
separator: {
|
|
251
|
+
height: StyleSheet.hairlineWidth,
|
|
252
|
+
marginRight: 16,
|
|
253
|
+
},
|
|
118
254
|
disabled: {
|
|
119
255
|
opacity: 0.45,
|
|
120
256
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { Entypo } from '@expo/vector-icons'
|
|
3
4
|
import * as Haptics from 'expo-haptics'
|
|
4
5
|
import { useTheme } from '../../theme'
|
|
5
6
|
|
|
@@ -49,7 +50,7 @@ export function MonthPicker({ value, onChange, style }: MonthPickerProps) {
|
|
|
49
50
|
activeOpacity={0.6}
|
|
50
51
|
touchSoundDisabled={true}
|
|
51
52
|
>
|
|
52
|
-
<
|
|
53
|
+
<Entypo name="chevron-left" size={22} color={colors.foreground} />
|
|
53
54
|
</TouchableOpacity>
|
|
54
55
|
<Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>
|
|
55
56
|
{MONTH_NAMES[value.month - 1]} {value.year}
|
|
@@ -60,7 +61,7 @@ export function MonthPicker({ value, onChange, style }: MonthPickerProps) {
|
|
|
60
61
|
activeOpacity={0.6}
|
|
61
62
|
touchSoundDisabled={true}
|
|
62
63
|
>
|
|
63
|
-
<
|
|
64
|
+
<Entypo name="chevron-right" size={22} color={colors.foreground} />
|
|
64
65
|
</TouchableOpacity>
|
|
65
66
|
</View>
|
|
66
67
|
)
|
|
@@ -78,10 +79,6 @@ const styles = StyleSheet.create({
|
|
|
78
79
|
alignItems: 'center',
|
|
79
80
|
justifyContent: 'center',
|
|
80
81
|
},
|
|
81
|
-
arrowText: {
|
|
82
|
-
fontSize: 24,
|
|
83
|
-
lineHeight: 30,
|
|
84
|
-
},
|
|
85
82
|
label: {
|
|
86
83
|
fontSize: 17,
|
|
87
84
|
fontWeight: '500',
|
|
@@ -101,7 +101,7 @@ export function RadioGroup({
|
|
|
101
101
|
|
|
102
102
|
const styles = StyleSheet.create({
|
|
103
103
|
container: {
|
|
104
|
-
gap:
|
|
104
|
+
gap: 12,
|
|
105
105
|
},
|
|
106
106
|
horizontal: {
|
|
107
107
|
flexDirection: 'row',
|
|
@@ -110,7 +110,7 @@ const styles = StyleSheet.create({
|
|
|
110
110
|
row: {
|
|
111
111
|
flexDirection: 'row',
|
|
112
112
|
alignItems: 'center',
|
|
113
|
-
gap:
|
|
113
|
+
gap: 12,
|
|
114
114
|
},
|
|
115
115
|
radio: {
|
|
116
116
|
width: 24,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react'
|
|
2
2
|
import { View, Text, TouchableOpacity, Modal, Animated, StyleSheet, ViewStyle, Platform } from 'react-native'
|
|
3
3
|
import { Picker } from '@react-native-picker/picker'
|
|
4
|
+
import { Entypo } from '@expo/vector-icons'
|
|
4
5
|
import * as Haptics from 'expo-haptics'
|
|
5
6
|
import { useTheme } from '../../theme'
|
|
6
7
|
|
|
@@ -106,7 +107,7 @@ export function Select({
|
|
|
106
107
|
>
|
|
107
108
|
{selected?.label ?? placeholder}
|
|
108
109
|
</Text>
|
|
109
|
-
<
|
|
110
|
+
<Entypo name="chevron-with-circle-down" size={20} color={colors.mutedForeground} />
|
|
110
111
|
</TouchableOpacity>
|
|
111
112
|
</Animated.View>
|
|
112
113
|
) : null}
|
|
@@ -226,28 +227,31 @@ export function Select({
|
|
|
226
227
|
|
|
227
228
|
const styles = StyleSheet.create({
|
|
228
229
|
container: {
|
|
229
|
-
gap:
|
|
230
|
+
gap: 8,
|
|
230
231
|
},
|
|
231
232
|
label: {
|
|
232
233
|
fontSize: 15,
|
|
233
234
|
fontWeight: '500',
|
|
234
|
-
marginBottom: 2,
|
|
235
235
|
},
|
|
236
236
|
trigger: {
|
|
237
237
|
flexDirection: 'row',
|
|
238
238
|
alignItems: 'center',
|
|
239
239
|
justifyContent: 'space-between',
|
|
240
240
|
borderWidth: 1.5,
|
|
241
|
-
borderRadius:
|
|
242
|
-
paddingHorizontal:
|
|
243
|
-
paddingVertical:
|
|
241
|
+
borderRadius: 8,
|
|
242
|
+
paddingHorizontal: 16,
|
|
243
|
+
paddingVertical: 14,
|
|
244
|
+
shadowColor: '#000',
|
|
245
|
+
shadowOffset: { width: 0, height: 1 },
|
|
246
|
+
shadowOpacity: 0.04,
|
|
247
|
+
shadowRadius: 2,
|
|
248
|
+
elevation: 1,
|
|
244
249
|
},
|
|
245
250
|
triggerText: {
|
|
246
251
|
fontSize: 17,
|
|
247
252
|
flex: 1,
|
|
248
253
|
},
|
|
249
254
|
chevron: {
|
|
250
|
-
fontSize: 16,
|
|
251
255
|
marginLeft: 8,
|
|
252
256
|
},
|
|
253
257
|
helperText: {
|
|
@@ -258,15 +262,15 @@ const styles = StyleSheet.create({
|
|
|
258
262
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
|
259
263
|
},
|
|
260
264
|
iosSheet: {
|
|
261
|
-
borderTopLeftRadius:
|
|
262
|
-
borderTopRightRadius:
|
|
265
|
+
borderTopLeftRadius: 16,
|
|
266
|
+
borderTopRightRadius: 16,
|
|
263
267
|
paddingBottom: 32,
|
|
264
268
|
},
|
|
265
269
|
iosToolbar: {
|
|
266
270
|
flexDirection: 'row',
|
|
267
271
|
alignItems: 'center',
|
|
268
272
|
justifyContent: 'space-between',
|
|
269
|
-
paddingHorizontal:
|
|
273
|
+
paddingHorizontal: 16,
|
|
270
274
|
paddingVertical: 12,
|
|
271
275
|
borderBottomWidth: 1,
|
|
272
276
|
},
|
|
@@ -288,9 +292,9 @@ const styles = StyleSheet.create({
|
|
|
288
292
|
},
|
|
289
293
|
webPicker: {
|
|
290
294
|
borderWidth: 1.5,
|
|
291
|
-
borderRadius:
|
|
292
|
-
paddingHorizontal:
|
|
293
|
-
paddingVertical:
|
|
295
|
+
borderRadius: 8,
|
|
296
|
+
paddingHorizontal: 16,
|
|
297
|
+
paddingVertical: 14,
|
|
294
298
|
fontSize: 17,
|
|
295
299
|
},
|
|
296
300
|
})
|
|
@@ -79,18 +79,17 @@ export function Textarea({
|
|
|
79
79
|
|
|
80
80
|
const styles = StyleSheet.create({
|
|
81
81
|
container: {
|
|
82
|
-
gap:
|
|
82
|
+
gap: 8,
|
|
83
83
|
},
|
|
84
84
|
label: {
|
|
85
85
|
fontSize: 15,
|
|
86
86
|
fontWeight: '500',
|
|
87
|
-
marginBottom: 6,
|
|
88
87
|
},
|
|
89
88
|
input: {
|
|
90
89
|
borderWidth: 1.5,
|
|
91
|
-
borderRadius:
|
|
92
|
-
paddingHorizontal:
|
|
93
|
-
paddingVertical:
|
|
90
|
+
borderRadius: 8,
|
|
91
|
+
paddingHorizontal: 16,
|
|
92
|
+
paddingVertical: 14,
|
|
94
93
|
fontSize: 17,
|
|
95
94
|
},
|
|
96
95
|
helperText: {
|