@retray-dev/ui-kit 2.5.1 → 2.6.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 +153 -6
- package/dist/index.d.mts +98 -8
- package/dist/index.d.ts +98 -8
- package/dist/index.js +591 -505
- package/dist/index.mjs +533 -436
- package/package.json +23 -21
- package/src/components/Accordion/Accordion.tsx +61 -57
- package/src/components/Alert/Alert.tsx +11 -10
- package/src/components/AlertBanner/AlertBanner.tsx +23 -10
- package/src/components/Avatar/Avatar.tsx +9 -8
- package/src/components/Badge/Badge.tsx +27 -12
- package/src/components/Button/Button.tsx +30 -12
- package/src/components/Card/Card.tsx +12 -11
- package/src/components/Checkbox/Checkbox.tsx +16 -13
- package/src/components/Chip/Chip.tsx +8 -7
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +12 -11
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +2 -1
- package/src/components/CurrencyInput/CurrencyInput.tsx +2 -1
- package/src/components/EmptyState/EmptyState.tsx +34 -21
- package/src/components/Input/Input.tsx +44 -22
- package/src/components/LabelValue/LabelValue.tsx +6 -5
- package/src/components/ListItem/ListItem.tsx +46 -22
- package/src/components/MonthPicker/MonthPicker.tsx +9 -8
- package/src/components/Progress/Progress.tsx +2 -1
- package/src/components/RadioGroup/RadioGroup.tsx +18 -15
- package/src/components/Select/Select.tsx +25 -24
- package/src/components/Sheet/Sheet.tsx +15 -14
- package/src/components/Slider/Slider.tsx +7 -6
- package/src/components/Switch/Switch.tsx +7 -6
- package/src/components/Tabs/Tabs.tsx +17 -14
- package/src/components/Text/Text.tsx +7 -6
- package/src/components/Textarea/Textarea.tsx +9 -8
- package/src/components/Toast/Toast.tsx +30 -19
- package/src/components/Toggle/Toggle.tsx +36 -10
- package/src/index.ts +4 -0
- package/src/utils/haptics.ts +32 -0
- package/src/utils/icons.ts +73 -0
- package/src/utils/scaling.ts +26 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useRef, useState, useEffect } from 'react'
|
|
2
2
|
import { View, Animated, StyleSheet, ViewStyle } from 'react-native'
|
|
3
3
|
import { useTheme } from '../../theme'
|
|
4
|
+
import { vs } from '../../utils/scaling'
|
|
4
5
|
|
|
5
6
|
export interface ProgressProps {
|
|
6
7
|
/** Current progress value. Clamped to `[0, max]`. Defaults to `0`. */
|
|
@@ -40,7 +41,7 @@ export function Progress({ value = 0, max = 100, style }: ProgressProps) {
|
|
|
40
41
|
|
|
41
42
|
const styles = StyleSheet.create({
|
|
42
43
|
track: {
|
|
43
|
-
height: 8,
|
|
44
|
+
height: vs(8),
|
|
44
45
|
borderRadius: 999,
|
|
45
46
|
overflow: 'hidden',
|
|
46
47
|
width: '100%',
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React, { useRef } from 'react'
|
|
2
|
-
import { TouchableOpacity, Animated, View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
-
import
|
|
2
|
+
import { TouchableOpacity, Animated, View, Text, StyleSheet, ViewStyle, Platform } from 'react-native'
|
|
3
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
4
|
+
|
|
5
|
+
const nativeDriver = Platform.OS !== 'web'
|
|
4
6
|
import { useTheme } from '../../theme'
|
|
7
|
+
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
5
8
|
|
|
6
9
|
export interface RadioOption {
|
|
7
10
|
label: string
|
|
@@ -31,11 +34,11 @@ function RadioItem({
|
|
|
31
34
|
|
|
32
35
|
const handlePressIn = () => {
|
|
33
36
|
if (option.disabled) return
|
|
34
|
-
Animated.spring(scale, { toValue: 0.95, useNativeDriver:
|
|
37
|
+
Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver, speed: 40, bounciness: 0 }).start()
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
const handlePressOut = () => {
|
|
38
|
-
Animated.spring(scale, { toValue: 1, useNativeDriver:
|
|
41
|
+
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, speed: 40, bounciness: 4 }).start()
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
return (
|
|
@@ -44,7 +47,7 @@ function RadioItem({
|
|
|
44
47
|
style={styles.row}
|
|
45
48
|
onPress={() => {
|
|
46
49
|
if (!option.disabled) {
|
|
47
|
-
|
|
50
|
+
hapticSelection()
|
|
48
51
|
onSelect()
|
|
49
52
|
}
|
|
50
53
|
}}
|
|
@@ -101,7 +104,7 @@ export function RadioGroup({
|
|
|
101
104
|
|
|
102
105
|
const styles = StyleSheet.create({
|
|
103
106
|
container: {
|
|
104
|
-
gap: 12,
|
|
107
|
+
gap: vs(12),
|
|
105
108
|
},
|
|
106
109
|
horizontal: {
|
|
107
110
|
flexDirection: 'row',
|
|
@@ -110,23 +113,23 @@ const styles = StyleSheet.create({
|
|
|
110
113
|
row: {
|
|
111
114
|
flexDirection: 'row',
|
|
112
115
|
alignItems: 'center',
|
|
113
|
-
gap: 12,
|
|
116
|
+
gap: s(12),
|
|
114
117
|
},
|
|
115
118
|
radio: {
|
|
116
|
-
width: 24,
|
|
117
|
-
height: 24,
|
|
118
|
-
borderRadius: 12,
|
|
119
|
+
width: s(24),
|
|
120
|
+
height: s(24),
|
|
121
|
+
borderRadius: s(12),
|
|
119
122
|
borderWidth: 1.5,
|
|
120
123
|
alignItems: 'center',
|
|
121
124
|
justifyContent: 'center',
|
|
122
125
|
},
|
|
123
126
|
dot: {
|
|
124
|
-
width: 10,
|
|
125
|
-
height: 10,
|
|
126
|
-
borderRadius: 5,
|
|
127
|
+
width: s(10),
|
|
128
|
+
height: s(10),
|
|
129
|
+
borderRadius: s(5),
|
|
127
130
|
},
|
|
128
131
|
label: {
|
|
129
|
-
fontSize: 14,
|
|
130
|
-
lineHeight: 20,
|
|
132
|
+
fontSize: ms(14),
|
|
133
|
+
lineHeight: mvs(20),
|
|
131
134
|
},
|
|
132
135
|
})
|
|
@@ -2,8 +2,9 @@ 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
4
|
import { Entypo } from '@expo/vector-icons'
|
|
5
|
-
import
|
|
5
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
6
6
|
import { useTheme } from '../../theme'
|
|
7
|
+
import { s, vs, ms } from '../../utils/scaling'
|
|
7
8
|
|
|
8
9
|
const isIOS = Platform.OS === 'ios'
|
|
9
10
|
const isAndroid = Platform.OS === 'android'
|
|
@@ -55,7 +56,7 @@ export function Select({
|
|
|
55
56
|
|
|
56
57
|
const handleOpen = () => {
|
|
57
58
|
if (disabled) return
|
|
58
|
-
|
|
59
|
+
hapticSelection()
|
|
59
60
|
if (isIOS) {
|
|
60
61
|
setPendingValue(value)
|
|
61
62
|
setPickerVisible(true)
|
|
@@ -70,7 +71,7 @@ export function Select({
|
|
|
70
71
|
|
|
71
72
|
const handleConfirm = () => {
|
|
72
73
|
if (pendingValue !== undefined && pendingValue !== '') {
|
|
73
|
-
|
|
74
|
+
hapticSelection()
|
|
74
75
|
onValueChange?.(pendingValue)
|
|
75
76
|
}
|
|
76
77
|
setPickerVisible(false)
|
|
@@ -165,7 +166,7 @@ export function Select({
|
|
|
165
166
|
selectedValue={value ?? ''}
|
|
166
167
|
onValueChange={(val) => {
|
|
167
168
|
if (val !== '') {
|
|
168
|
-
|
|
169
|
+
hapticSelection()
|
|
169
170
|
onValueChange?.(val as string)
|
|
170
171
|
}
|
|
171
172
|
}}
|
|
@@ -227,10 +228,10 @@ export function Select({
|
|
|
227
228
|
|
|
228
229
|
const styles = StyleSheet.create({
|
|
229
230
|
container: {
|
|
230
|
-
gap: 8,
|
|
231
|
+
gap: vs(8),
|
|
231
232
|
},
|
|
232
233
|
label: {
|
|
233
|
-
fontSize: 15,
|
|
234
|
+
fontSize: ms(15),
|
|
234
235
|
fontWeight: '500',
|
|
235
236
|
},
|
|
236
237
|
trigger: {
|
|
@@ -238,9 +239,9 @@ const styles = StyleSheet.create({
|
|
|
238
239
|
alignItems: 'center',
|
|
239
240
|
justifyContent: 'space-between',
|
|
240
241
|
borderWidth: 1.5,
|
|
241
|
-
borderRadius: 8,
|
|
242
|
-
paddingHorizontal: 16,
|
|
243
|
-
paddingVertical: 14,
|
|
242
|
+
borderRadius: ms(8),
|
|
243
|
+
paddingHorizontal: s(16),
|
|
244
|
+
paddingVertical: vs(14),
|
|
244
245
|
shadowColor: '#000',
|
|
245
246
|
shadowOffset: { width: 0, height: 1 },
|
|
246
247
|
shadowOpacity: 0.04,
|
|
@@ -248,41 +249,41 @@ const styles = StyleSheet.create({
|
|
|
248
249
|
elevation: 1,
|
|
249
250
|
},
|
|
250
251
|
triggerText: {
|
|
251
|
-
fontSize: 17,
|
|
252
|
+
fontSize: ms(17),
|
|
252
253
|
flex: 1,
|
|
253
254
|
},
|
|
254
255
|
chevron: {
|
|
255
|
-
marginLeft: 8,
|
|
256
|
+
marginLeft: s(8),
|
|
256
257
|
},
|
|
257
258
|
helperText: {
|
|
258
|
-
fontSize: 13,
|
|
259
|
+
fontSize: ms(13),
|
|
259
260
|
},
|
|
260
261
|
iosBackdrop: {
|
|
261
262
|
flex: 1,
|
|
262
263
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
|
263
264
|
},
|
|
264
265
|
iosSheet: {
|
|
265
|
-
borderTopLeftRadius: 16,
|
|
266
|
-
borderTopRightRadius: 16,
|
|
267
|
-
paddingBottom: 32,
|
|
266
|
+
borderTopLeftRadius: ms(16),
|
|
267
|
+
borderTopRightRadius: ms(16),
|
|
268
|
+
paddingBottom: vs(32),
|
|
268
269
|
},
|
|
269
270
|
iosToolbar: {
|
|
270
271
|
flexDirection: 'row',
|
|
271
272
|
alignItems: 'center',
|
|
272
273
|
justifyContent: 'space-between',
|
|
273
|
-
paddingHorizontal: 16,
|
|
274
|
-
paddingVertical: 12,
|
|
274
|
+
paddingHorizontal: s(16),
|
|
275
|
+
paddingVertical: vs(12),
|
|
275
276
|
borderBottomWidth: 1,
|
|
276
277
|
},
|
|
277
278
|
iosToolbarTitle: {
|
|
278
|
-
fontSize: 17,
|
|
279
|
+
fontSize: ms(17),
|
|
279
280
|
fontWeight: '600',
|
|
280
281
|
},
|
|
281
282
|
iosDoneBtn: {
|
|
282
|
-
padding: 4,
|
|
283
|
+
padding: s(4),
|
|
283
284
|
},
|
|
284
285
|
iosDoneBtnText: {
|
|
285
|
-
fontSize: 17,
|
|
286
|
+
fontSize: ms(17),
|
|
286
287
|
fontWeight: '600',
|
|
287
288
|
},
|
|
288
289
|
androidHiddenPicker: {
|
|
@@ -292,9 +293,9 @@ const styles = StyleSheet.create({
|
|
|
292
293
|
},
|
|
293
294
|
webPicker: {
|
|
294
295
|
borderWidth: 1.5,
|
|
295
|
-
borderRadius: 8,
|
|
296
|
-
paddingHorizontal: 16,
|
|
297
|
-
paddingVertical: 14,
|
|
298
|
-
fontSize: 17,
|
|
296
|
+
borderRadius: ms(8),
|
|
297
|
+
paddingHorizontal: s(16),
|
|
298
|
+
paddingVertical: vs(14),
|
|
299
|
+
fontSize: ms(17),
|
|
299
300
|
},
|
|
300
301
|
})
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
BottomSheetModalProvider,
|
|
8
8
|
type BottomSheetBackdropProps,
|
|
9
9
|
} from '@gorhom/bottom-sheet'
|
|
10
|
-
import
|
|
10
|
+
import { impactLight } from '../../utils/haptics'
|
|
11
11
|
import { useTheme } from '../../theme'
|
|
12
|
+
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
12
13
|
|
|
13
14
|
export { BottomSheetModalProvider }
|
|
14
15
|
|
|
@@ -41,7 +42,7 @@ export function Sheet({
|
|
|
41
42
|
|
|
42
43
|
useEffect(() => {
|
|
43
44
|
if (open) {
|
|
44
|
-
|
|
45
|
+
impactLight()
|
|
45
46
|
ref.current?.present()
|
|
46
47
|
} else {
|
|
47
48
|
ref.current?.dismiss()
|
|
@@ -88,28 +89,28 @@ export function Sheet({
|
|
|
88
89
|
|
|
89
90
|
const styles = StyleSheet.create({
|
|
90
91
|
background: {
|
|
91
|
-
borderTopLeftRadius: 16,
|
|
92
|
-
borderTopRightRadius: 16,
|
|
92
|
+
borderTopLeftRadius: ms(16),
|
|
93
|
+
borderTopRightRadius: ms(16),
|
|
93
94
|
},
|
|
94
95
|
handle: {
|
|
95
|
-
width: 36,
|
|
96
|
-
height: 4,
|
|
97
|
-
borderRadius: 2,
|
|
96
|
+
width: s(36),
|
|
97
|
+
height: vs(4),
|
|
98
|
+
borderRadius: ms(2),
|
|
98
99
|
},
|
|
99
100
|
content: {
|
|
100
|
-
paddingHorizontal: 24,
|
|
101
|
-
paddingBottom: 32,
|
|
101
|
+
paddingHorizontal: s(24),
|
|
102
|
+
paddingBottom: vs(32),
|
|
102
103
|
},
|
|
103
104
|
header: {
|
|
104
|
-
gap: 8,
|
|
105
|
-
marginBottom: 16,
|
|
105
|
+
gap: vs(8),
|
|
106
|
+
marginBottom: vs(16),
|
|
106
107
|
},
|
|
107
108
|
title: {
|
|
108
|
-
fontSize: 18,
|
|
109
|
+
fontSize: ms(18),
|
|
109
110
|
fontWeight: '600',
|
|
110
111
|
},
|
|
111
112
|
description: {
|
|
112
|
-
fontSize: 14,
|
|
113
|
-
lineHeight: 20,
|
|
113
|
+
fontSize: ms(14),
|
|
114
|
+
lineHeight: mvs(20),
|
|
114
115
|
},
|
|
115
116
|
})
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React, { useRef } from 'react'
|
|
2
2
|
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
3
|
import RNSlider from '@react-native-community/slider'
|
|
4
|
-
import
|
|
4
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
5
5
|
import { useTheme } from '../../theme'
|
|
6
|
+
import { vs, ms } from '../../utils/scaling'
|
|
6
7
|
|
|
7
8
|
export interface SliderProps {
|
|
8
9
|
value?: number
|
|
@@ -39,7 +40,7 @@ export function Slider({
|
|
|
39
40
|
const handleValueChange = (v: number) => {
|
|
40
41
|
if (step && v !== lastSteppedValue.current) {
|
|
41
42
|
lastSteppedValue.current = v
|
|
42
|
-
|
|
43
|
+
hapticSelection()
|
|
43
44
|
}
|
|
44
45
|
onValueChange?.(v)
|
|
45
46
|
}
|
|
@@ -82,7 +83,7 @@ export function Slider({
|
|
|
82
83
|
|
|
83
84
|
const styles = StyleSheet.create({
|
|
84
85
|
wrapper: {
|
|
85
|
-
gap: 8,
|
|
86
|
+
gap: vs(8),
|
|
86
87
|
},
|
|
87
88
|
header: {
|
|
88
89
|
flexDirection: 'row',
|
|
@@ -90,16 +91,16 @@ const styles = StyleSheet.create({
|
|
|
90
91
|
alignItems: 'center',
|
|
91
92
|
},
|
|
92
93
|
label: {
|
|
93
|
-
fontSize: 15,
|
|
94
|
+
fontSize: ms(15),
|
|
94
95
|
fontWeight: '500',
|
|
95
96
|
},
|
|
96
97
|
valueText: {
|
|
97
|
-
fontSize: 14,
|
|
98
|
+
fontSize: ms(14),
|
|
98
99
|
fontWeight: '500',
|
|
99
100
|
},
|
|
100
101
|
slider: {
|
|
101
102
|
width: '100%',
|
|
102
|
-
height: 40,
|
|
103
|
+
height: vs(40),
|
|
103
104
|
},
|
|
104
105
|
disabled: {
|
|
105
106
|
opacity: 0.45,
|
|
@@ -2,13 +2,14 @@ import React, { useEffect, useRef } from 'react'
|
|
|
2
2
|
import { TouchableOpacity, Animated, StyleSheet, ViewStyle, Platform, View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
const nativeDriver = Platform.OS !== 'web'
|
|
5
|
-
import
|
|
5
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
6
6
|
import { useTheme } from '../../theme'
|
|
7
|
+
import { s, vs } from '../../utils/scaling'
|
|
7
8
|
|
|
8
|
-
const TRACK_WIDTH = 60
|
|
9
|
-
const TRACK_HEIGHT = 36
|
|
10
|
-
const THUMB_SIZE = 28
|
|
11
|
-
const THUMB_OFFSET = 4
|
|
9
|
+
const TRACK_WIDTH = s(60)
|
|
10
|
+
const TRACK_HEIGHT = vs(36)
|
|
11
|
+
const THUMB_SIZE = s(28)
|
|
12
|
+
const THUMB_OFFSET = s(4)
|
|
12
13
|
const THUMB_TRAVEL = TRACK_WIDTH - THUMB_SIZE - THUMB_OFFSET * 2
|
|
13
14
|
|
|
14
15
|
export interface SwitchProps {
|
|
@@ -47,7 +48,7 @@ export function Switch({ checked = false, onCheckedChange, disabled, style }: Sw
|
|
|
47
48
|
<View style={[{ opacity: disabled ? 0.45 : 1 }, style]}>
|
|
48
49
|
<TouchableOpacity
|
|
49
50
|
onPress={() => {
|
|
50
|
-
|
|
51
|
+
hapticSelection()
|
|
51
52
|
onCheckedChange?.(!checked)
|
|
52
53
|
}}
|
|
53
54
|
disabled={disabled}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react'
|
|
2
|
-
import { View, TouchableOpacity, Text, Animated, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
-
import
|
|
2
|
+
import { View, TouchableOpacity, Text, Animated, StyleSheet, ViewStyle, Platform } from 'react-native'
|
|
3
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
4
|
+
|
|
5
|
+
const nativeDriver = Platform.OS !== 'web'
|
|
4
6
|
import { useTheme } from '../../theme'
|
|
7
|
+
import { s, vs, ms } from '../../utils/scaling'
|
|
5
8
|
|
|
6
9
|
export interface TabItem {
|
|
7
10
|
label: string
|
|
@@ -43,11 +46,11 @@ function TabTrigger({
|
|
|
43
46
|
const scale = useRef(new Animated.Value(1)).current
|
|
44
47
|
|
|
45
48
|
const handlePressIn = () => {
|
|
46
|
-
Animated.spring(scale, { toValue: 0.95, useNativeDriver:
|
|
49
|
+
Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver, speed: 40, bounciness: 0 }).start()
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
const handlePressOut = () => {
|
|
50
|
-
Animated.spring(scale, { toValue: 1, useNativeDriver:
|
|
53
|
+
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, speed: 40, bounciness: 4 }).start()
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
return (
|
|
@@ -123,7 +126,7 @@ export function Tabs({ tabs, value, onValueChange, children, style }: TabsProps)
|
|
|
123
126
|
}, [active])
|
|
124
127
|
|
|
125
128
|
const handlePress = (v: string) => {
|
|
126
|
-
|
|
129
|
+
hapticSelection()
|
|
127
130
|
if (!value) setInternal(v)
|
|
128
131
|
onValueChange?.(v)
|
|
129
132
|
}
|
|
@@ -180,16 +183,16 @@ export function TabsContent({ value, activeValue, children, style }: TabsContent
|
|
|
180
183
|
const styles = StyleSheet.create({
|
|
181
184
|
list: {
|
|
182
185
|
flexDirection: 'row',
|
|
183
|
-
borderRadius: 12,
|
|
184
|
-
padding: 4,
|
|
185
|
-
gap: 4,
|
|
186
|
+
borderRadius: ms(12),
|
|
187
|
+
padding: s(4),
|
|
188
|
+
gap: s(4),
|
|
186
189
|
},
|
|
187
190
|
pill: {},
|
|
188
191
|
trigger: {
|
|
189
192
|
flex: 1,
|
|
190
|
-
paddingVertical: 12,
|
|
191
|
-
paddingHorizontal: 16,
|
|
192
|
-
borderRadius: 8,
|
|
193
|
+
paddingVertical: vs(12),
|
|
194
|
+
paddingHorizontal: s(16),
|
|
195
|
+
borderRadius: ms(8),
|
|
193
196
|
alignItems: 'center',
|
|
194
197
|
justifyContent: 'center',
|
|
195
198
|
zIndex: 1,
|
|
@@ -198,15 +201,15 @@ const styles = StyleSheet.create({
|
|
|
198
201
|
flexDirection: 'row',
|
|
199
202
|
alignItems: 'center',
|
|
200
203
|
justifyContent: 'center',
|
|
201
|
-
gap: 8,
|
|
204
|
+
gap: s(8),
|
|
202
205
|
},
|
|
203
206
|
triggerIcon: {
|
|
204
|
-
marginRight: 6,
|
|
207
|
+
marginRight: s(6),
|
|
205
208
|
alignItems: 'center',
|
|
206
209
|
justifyContent: 'center',
|
|
207
210
|
},
|
|
208
211
|
triggerLabel: {
|
|
209
|
-
fontSize: 15,
|
|
212
|
+
fontSize: ms(15),
|
|
210
213
|
fontWeight: '400',
|
|
211
214
|
},
|
|
212
215
|
activeTriggerLabel: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { Text as RNText, TextProps as RNTextProps, TextStyle } from 'react-native'
|
|
3
3
|
import { useTheme } from '../../theme'
|
|
4
|
+
import { ms, mvs } from '../../utils/scaling'
|
|
4
5
|
|
|
5
6
|
export type TextVariant = 'h1' | 'h2' | 'h3' | 'body' | 'caption' | 'label'
|
|
6
7
|
|
|
@@ -10,12 +11,12 @@ export interface TextProps extends RNTextProps {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const variantStyles: Record<TextVariant, TextStyle> = {
|
|
13
|
-
h1: { fontSize: 40, fontWeight: '700', lineHeight: 52 },
|
|
14
|
-
h2: { fontSize: 28, fontWeight: '700', lineHeight: 36 },
|
|
15
|
-
h3: { fontSize: 22, fontWeight: '600', lineHeight: 30 },
|
|
16
|
-
body: { fontSize: 17, fontWeight: '400', lineHeight: 26 },
|
|
17
|
-
caption: { fontSize: 13, fontWeight: '400', lineHeight: 20 },
|
|
18
|
-
label: { fontSize: 15, fontWeight: '500', lineHeight: 22 },
|
|
14
|
+
h1: { fontSize: ms(40), fontWeight: '700', lineHeight: mvs(52) },
|
|
15
|
+
h2: { fontSize: ms(28), fontWeight: '700', lineHeight: mvs(36) },
|
|
16
|
+
h3: { fontSize: ms(22), fontWeight: '600', lineHeight: mvs(30) },
|
|
17
|
+
body: { fontSize: ms(17), fontWeight: '400', lineHeight: mvs(26) },
|
|
18
|
+
caption: { fontSize: ms(13), fontWeight: '400', lineHeight: mvs(20) },
|
|
19
|
+
label: { fontSize: ms(15), fontWeight: '500', lineHeight: mvs(22) },
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function Text({ variant = 'body', color, style, children, ...props }: TextProps) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle, Platform } from 'react-native'
|
|
3
3
|
import { useTheme } from '../../theme'
|
|
4
|
+
import { s, vs, ms } from '../../utils/scaling'
|
|
4
5
|
|
|
5
6
|
const webInputResetStyle: any =
|
|
6
7
|
Platform.OS === 'web'
|
|
@@ -50,7 +51,7 @@ export function Textarea({
|
|
|
50
51
|
: colors.border,
|
|
51
52
|
color: colors.foreground,
|
|
52
53
|
backgroundColor: colors.background,
|
|
53
|
-
minHeight: rows * 30,
|
|
54
|
+
minHeight: rows * vs(30),
|
|
54
55
|
},
|
|
55
56
|
webInputResetStyle,
|
|
56
57
|
style,
|
|
@@ -79,20 +80,20 @@ export function Textarea({
|
|
|
79
80
|
|
|
80
81
|
const styles = StyleSheet.create({
|
|
81
82
|
container: {
|
|
82
|
-
gap: 8,
|
|
83
|
+
gap: vs(8),
|
|
83
84
|
},
|
|
84
85
|
label: {
|
|
85
|
-
fontSize: 15,
|
|
86
|
+
fontSize: ms(15),
|
|
86
87
|
fontWeight: '500',
|
|
87
88
|
},
|
|
88
89
|
input: {
|
|
89
90
|
borderWidth: 1.5,
|
|
90
|
-
borderRadius: 8,
|
|
91
|
-
paddingHorizontal: 16,
|
|
92
|
-
paddingVertical: 14,
|
|
93
|
-
fontSize: 17,
|
|
91
|
+
borderRadius: ms(8),
|
|
92
|
+
paddingHorizontal: s(16),
|
|
93
|
+
paddingVertical: vs(14),
|
|
94
|
+
fontSize: ms(17),
|
|
94
95
|
},
|
|
95
96
|
helperText: {
|
|
96
|
-
fontSize: 13,
|
|
97
|
+
fontSize: ms(13),
|
|
97
98
|
},
|
|
98
99
|
})
|
|
@@ -11,8 +11,10 @@ import Animated, {
|
|
|
11
11
|
import { scheduleOnRN } from 'react-native-worklets'
|
|
12
12
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler'
|
|
13
13
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
14
|
-
import
|
|
14
|
+
import { notificationSuccess, notificationError, impactLight } from '../../utils/haptics'
|
|
15
15
|
import { useTheme } from '../../theme'
|
|
16
|
+
import { s, vs, ms } from '../../utils/scaling'
|
|
17
|
+
import { renderIcon } from '../../utils/icons'
|
|
16
18
|
|
|
17
19
|
export type ToastVariant = 'default' | 'destructive' | 'success'
|
|
18
20
|
|
|
@@ -22,6 +24,13 @@ export interface ToastItem {
|
|
|
22
24
|
description?: string
|
|
23
25
|
variant?: ToastVariant
|
|
24
26
|
icon?: React.ReactNode
|
|
27
|
+
/**
|
|
28
|
+
* Icon name from `@expo/vector-icons`. See https://icons.expo.fyi.
|
|
29
|
+
* Takes precedence over `icon`. When neither is set, a default variant icon is shown.
|
|
30
|
+
*/
|
|
31
|
+
iconName?: string
|
|
32
|
+
/** Override the resolved icon color. Defaults to the variant text color. */
|
|
33
|
+
iconColor?: string
|
|
25
34
|
/** Auto-dismiss delay in milliseconds. Defaults to `3000`. */
|
|
26
35
|
duration?: number
|
|
27
36
|
}
|
|
@@ -108,7 +117,9 @@ function ToastNotification({ item, onDismiss }: { item: ToastItem; onDismiss: ()
|
|
|
108
117
|
<Entypo name="info-with-circle" size={22} color={textColor} />
|
|
109
118
|
)
|
|
110
119
|
|
|
111
|
-
const leftIcon = item.
|
|
120
|
+
const leftIcon: React.ReactNode = item.iconName
|
|
121
|
+
? renderIcon(item.iconName, 22, item.iconColor ?? textColor)
|
|
122
|
+
: item.icon ?? defaultIcon
|
|
112
123
|
|
|
113
124
|
return (
|
|
114
125
|
<GestureDetector gesture={panGesture}>
|
|
@@ -148,11 +159,11 @@ export function ToastProvider({ children }: ToastProviderProps) {
|
|
|
148
159
|
const toast = useCallback((item: Omit<ToastItem, 'id'>) => {
|
|
149
160
|
const id = Math.random().toString(36).slice(2)
|
|
150
161
|
if (item.variant === 'success') {
|
|
151
|
-
|
|
162
|
+
notificationSuccess()
|
|
152
163
|
} else if (item.variant === 'destructive') {
|
|
153
|
-
|
|
164
|
+
notificationError()
|
|
154
165
|
} else {
|
|
155
|
-
|
|
166
|
+
impactLight()
|
|
156
167
|
}
|
|
157
168
|
setToasts((prev) => [{ ...item, id }, ...prev].slice(0, 3))
|
|
158
169
|
}, [])
|
|
@@ -176,23 +187,23 @@ export function ToastProvider({ children }: ToastProviderProps) {
|
|
|
176
187
|
const styles = StyleSheet.create({
|
|
177
188
|
container: {
|
|
178
189
|
position: 'absolute',
|
|
179
|
-
left: 16,
|
|
180
|
-
right: 16,
|
|
181
|
-
gap: 8,
|
|
190
|
+
left: s(16),
|
|
191
|
+
right: s(16),
|
|
192
|
+
gap: vs(8),
|
|
182
193
|
zIndex: 9999,
|
|
183
194
|
},
|
|
184
195
|
containerWeb: {
|
|
185
196
|
left: undefined,
|
|
186
197
|
right: undefined,
|
|
187
198
|
alignSelf: 'center',
|
|
188
|
-
width: 400,
|
|
199
|
+
width: s(400),
|
|
189
200
|
},
|
|
190
201
|
toast: {
|
|
191
202
|
flexDirection: 'row',
|
|
192
203
|
alignItems: 'center',
|
|
193
|
-
borderRadius: 16,
|
|
194
|
-
paddingHorizontal: 20,
|
|
195
|
-
paddingVertical: 14,
|
|
204
|
+
borderRadius: ms(16),
|
|
205
|
+
paddingHorizontal: s(20),
|
|
206
|
+
paddingVertical: vs(14),
|
|
196
207
|
shadowColor: '#000',
|
|
197
208
|
shadowOffset: { width: 0, height: 4 },
|
|
198
209
|
shadowOpacity: 0.15,
|
|
@@ -201,23 +212,23 @@ const styles = StyleSheet.create({
|
|
|
201
212
|
},
|
|
202
213
|
toastContent: {
|
|
203
214
|
flex: 1,
|
|
204
|
-
gap: 4,
|
|
215
|
+
gap: vs(4),
|
|
205
216
|
},
|
|
206
217
|
leftIconContainer: {
|
|
207
|
-
width: 40,
|
|
218
|
+
width: s(40),
|
|
208
219
|
alignItems: 'center',
|
|
209
220
|
justifyContent: 'center',
|
|
210
|
-
marginRight: 8,
|
|
221
|
+
marginRight: s(8),
|
|
211
222
|
},
|
|
212
223
|
toastTitle: {
|
|
213
|
-
fontSize: 15,
|
|
224
|
+
fontSize: ms(15),
|
|
214
225
|
fontWeight: '600',
|
|
215
226
|
},
|
|
216
227
|
toastDescription: {
|
|
217
|
-
fontSize: 14,
|
|
228
|
+
fontSize: ms(14),
|
|
218
229
|
},
|
|
219
230
|
dismissButton: {
|
|
220
|
-
padding: 8,
|
|
221
|
-
marginLeft: 4,
|
|
231
|
+
padding: s(8),
|
|
232
|
+
marginLeft: s(4),
|
|
222
233
|
},
|
|
223
234
|
})
|