@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.
Files changed (38) hide show
  1. package/COMPONENTS.md +153 -6
  2. package/dist/index.d.mts +98 -8
  3. package/dist/index.d.ts +98 -8
  4. package/dist/index.js +591 -505
  5. package/dist/index.mjs +533 -436
  6. package/package.json +23 -21
  7. package/src/components/Accordion/Accordion.tsx +61 -57
  8. package/src/components/Alert/Alert.tsx +11 -10
  9. package/src/components/AlertBanner/AlertBanner.tsx +23 -10
  10. package/src/components/Avatar/Avatar.tsx +9 -8
  11. package/src/components/Badge/Badge.tsx +27 -12
  12. package/src/components/Button/Button.tsx +30 -12
  13. package/src/components/Card/Card.tsx +12 -11
  14. package/src/components/Checkbox/Checkbox.tsx +16 -13
  15. package/src/components/Chip/Chip.tsx +8 -7
  16. package/src/components/ConfirmDialog/ConfirmDialog.tsx +12 -11
  17. package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +2 -1
  18. package/src/components/CurrencyInput/CurrencyInput.tsx +2 -1
  19. package/src/components/EmptyState/EmptyState.tsx +34 -21
  20. package/src/components/Input/Input.tsx +44 -22
  21. package/src/components/LabelValue/LabelValue.tsx +6 -5
  22. package/src/components/ListItem/ListItem.tsx +46 -22
  23. package/src/components/MonthPicker/MonthPicker.tsx +9 -8
  24. package/src/components/Progress/Progress.tsx +2 -1
  25. package/src/components/RadioGroup/RadioGroup.tsx +18 -15
  26. package/src/components/Select/Select.tsx +25 -24
  27. package/src/components/Sheet/Sheet.tsx +15 -14
  28. package/src/components/Slider/Slider.tsx +7 -6
  29. package/src/components/Switch/Switch.tsx +7 -6
  30. package/src/components/Tabs/Tabs.tsx +17 -14
  31. package/src/components/Text/Text.tsx +7 -6
  32. package/src/components/Textarea/Textarea.tsx +9 -8
  33. package/src/components/Toast/Toast.tsx +30 -19
  34. package/src/components/Toggle/Toggle.tsx +36 -10
  35. package/src/index.ts +4 -0
  36. package/src/utils/haptics.ts +32 -0
  37. package/src/utils/icons.ts +73 -0
  38. 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 * as Haptics from 'expo-haptics'
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: true, speed: 40, bounciness: 0 }).start()
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: true, speed: 40, bounciness: 4 }).start()
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
- Haptics.selectionAsync()
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 * as Haptics from 'expo-haptics'
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
- Haptics.selectionAsync()
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
- Haptics.selectionAsync()
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
- Haptics.selectionAsync()
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 * as Haptics from 'expo-haptics'
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
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
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 * as Haptics from 'expo-haptics'
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
- Haptics.selectionAsync()
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 * as Haptics from 'expo-haptics'
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
- Haptics.selectionAsync()
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 * as Haptics from 'expo-haptics'
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: true, speed: 40, bounciness: 0 }).start()
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: true, speed: 40, bounciness: 4 }).start()
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
- Haptics.selectionAsync()
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 * as Haptics from 'expo-haptics'
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.icon ?? defaultIcon
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
- Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)
162
+ notificationSuccess()
152
163
  } else if (item.variant === 'destructive') {
153
- Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)
164
+ notificationError()
154
165
  } else {
155
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
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
  })