@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
@@ -1,14 +1,14 @@
1
- import React, { useRef, useCallback } from 'react'
2
- import { View, Text, TouchableOpacity, Animated, StyleSheet, ViewStyle } from 'react-native'
3
- import {
4
- BottomSheetModal,
5
- BottomSheetView,
6
- BottomSheetBackdrop,
7
- type BottomSheetBackdropProps,
8
- } from '@gorhom/bottom-sheet'
1
+ import React, { useRef, useState } from 'react'
2
+ import { View, Text, TouchableOpacity, Modal, Animated, StyleSheet, ViewStyle, Platform } from 'react-native'
3
+ import { Picker } from '@react-native-picker/picker'
4
+ import { Entypo } from '@expo/vector-icons'
9
5
  import * as Haptics from 'expo-haptics'
10
6
  import { useTheme } from '../../theme'
11
7
 
8
+ const isIOS = Platform.OS === 'ios'
9
+ const isAndroid = Platform.OS === 'android'
10
+ const isWeb = Platform.OS === 'web'
11
+
12
12
  export interface SelectOption {
13
13
  label: string
14
14
  value: string
@@ -19,10 +19,8 @@ export interface SelectProps {
19
19
  options: SelectOption[]
20
20
  value?: string
21
21
  onValueChange?: (value: string) => void
22
- /** Text shown when no option is selected. Defaults to `'Select an option'`. */
23
22
  placeholder?: string
24
23
  label?: string
25
- /** Red helper text; also changes trigger border to `destructive` color. */
26
24
  error?: string
27
25
  disabled?: boolean
28
26
  style?: ViewStyle
@@ -39,8 +37,10 @@ export function Select({
39
37
  style,
40
38
  }: SelectProps) {
41
39
  const { colors } = useTheme()
42
- const bottomSheetRef = useRef<BottomSheetModal>(null)
43
40
  const scale = useRef(new Animated.Value(1)).current
41
+ const [pickerVisible, setPickerVisible] = useState(false)
42
+ const [pendingValue, setPendingValue] = useState<string | undefined>(value)
43
+ const pickerRef = useRef<React.ElementRef<typeof Picker>>(null)
44
44
 
45
45
  const selected = options.find((o) => o.value === value)
46
46
 
@@ -54,179 +54,247 @@ export function Select({
54
54
  }
55
55
 
56
56
  const handleOpen = () => {
57
- if (!disabled) {
58
- Haptics.selectionAsync()
59
- bottomSheetRef.current?.present()
57
+ if (disabled) return
58
+ Haptics.selectionAsync()
59
+ if (isIOS) {
60
+ setPendingValue(value)
61
+ setPickerVisible(true)
62
+ } else if (isAndroid) {
63
+ ;(pickerRef.current as any)?.focus()
60
64
  }
61
65
  }
62
66
 
63
- const renderBackdrop = useCallback(
64
- (props: BottomSheetBackdropProps) => (
65
- <BottomSheetBackdrop
66
- {...props}
67
- disappearsOnIndex={-1}
68
- appearsOnIndex={0}
69
- pressBehavior="close"
70
- />
71
- ),
72
- []
73
- )
67
+ const handleDismiss = () => {
68
+ setPickerVisible(false)
69
+ }
70
+
71
+ const handleConfirm = () => {
72
+ if (pendingValue !== undefined && pendingValue !== '') {
73
+ Haptics.selectionAsync()
74
+ onValueChange?.(pendingValue)
75
+ }
76
+ setPickerVisible(false)
77
+ }
74
78
 
75
79
  return (
76
80
  <View style={[styles.container, style]}>
77
81
  {label ? <Text style={[styles.label, { color: colors.foreground }]}>{label}</Text> : null}
78
82
 
79
- <Animated.View style={{ transform: [{ scale }], opacity: disabled ? 0.45 : 1 }}>
80
- <TouchableOpacity
81
- style={[
82
- styles.trigger,
83
- {
84
- borderColor: error ? colors.destructive : colors.border,
85
- backgroundColor: colors.background,
86
- },
87
- ]}
88
- onPress={handleOpen}
89
- onPressIn={handlePressIn}
90
- onPressOut={handlePressOut}
91
- activeOpacity={1}
92
- touchSoundDisabled={true}
93
- >
94
- <Text
83
+ {/* Trigger button shown on iOS and Android only */}
84
+ {!isWeb ? (
85
+ <Animated.View style={{ transform: [{ scale }], opacity: disabled ? 0.45 : 1 }}>
86
+ <TouchableOpacity
87
+ style={[
88
+ styles.trigger,
89
+ {
90
+ borderColor: error ? colors.destructive : colors.border,
91
+ backgroundColor: colors.background,
92
+ },
93
+ ]}
94
+ onPress={handleOpen}
95
+ onPressIn={handlePressIn}
96
+ onPressOut={handlePressOut}
97
+ activeOpacity={1}
98
+ touchSoundDisabled={true}
99
+ >
100
+ <Text
101
+ style={[
102
+ styles.triggerText,
103
+ { color: selected ? colors.foreground : colors.mutedForeground },
104
+ ]}
105
+ numberOfLines={1}
106
+ allowFontScaling={true}
107
+ >
108
+ {selected?.label ?? placeholder}
109
+ </Text>
110
+ <Entypo name="chevron-with-circle-down" size={20} color={colors.mutedForeground} />
111
+ </TouchableOpacity>
112
+ </Animated.View>
113
+ ) : null}
114
+
115
+ {/* iOS: Modal with wheel Picker */}
116
+ {isIOS ? (
117
+ <Modal
118
+ visible={pickerVisible}
119
+ transparent
120
+ animationType="slide"
121
+ onRequestClose={handleDismiss}
122
+ >
123
+ <TouchableOpacity style={styles.iosBackdrop} activeOpacity={1} onPress={handleDismiss} />
124
+ <View style={[styles.iosSheet, { backgroundColor: colors.card }]}>
125
+ <View style={[styles.iosToolbar, { borderBottomColor: colors.border }]}>
126
+ {label ? (
127
+ <Text style={[styles.iosToolbarTitle, { color: colors.foreground }]} allowFontScaling={true}>
128
+ {label}
129
+ </Text>
130
+ ) : (
131
+ <View />
132
+ )}
133
+ <TouchableOpacity onPress={handleConfirm} style={styles.iosDoneBtn} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
134
+ <Text style={[styles.iosDoneBtnText, { color: colors.primary }]} allowFontScaling={true}>
135
+ Done
136
+ </Text>
137
+ </TouchableOpacity>
138
+ </View>
139
+ <Picker
140
+ selectedValue={pendingValue ?? ''}
141
+ onValueChange={(val) => setPendingValue(val as string)}
142
+ itemStyle={{ color: colors.foreground }}
143
+ >
144
+ {!value ? (
145
+ <Picker.Item label={placeholder} value="" color={colors.mutedForeground} enabled={false} />
146
+ ) : null}
147
+ {options.map((o) => (
148
+ <Picker.Item
149
+ key={o.value}
150
+ label={o.label}
151
+ value={o.value}
152
+ enabled={!o.disabled}
153
+ color={o.disabled ? colors.mutedForeground : colors.foreground}
154
+ />
155
+ ))}
156
+ </Picker>
157
+ </View>
158
+ </Modal>
159
+ ) : null}
160
+
161
+ {/* Android: hidden Picker opened programmatically via focus() */}
162
+ {isAndroid ? (
163
+ <Picker
164
+ ref={pickerRef}
165
+ selectedValue={value ?? ''}
166
+ onValueChange={(val) => {
167
+ if (val !== '') {
168
+ Haptics.selectionAsync()
169
+ onValueChange?.(val as string)
170
+ }
171
+ }}
172
+ mode="dialog"
173
+ enabled={!disabled}
174
+ prompt={label}
175
+ style={styles.androidHiddenPicker}
176
+ >
177
+ {!value ? <Picker.Item label={placeholder} value="" enabled={false} /> : null}
178
+ {options.map((o) => (
179
+ <Picker.Item
180
+ key={o.value}
181
+ label={o.label}
182
+ value={o.value}
183
+ enabled={!o.disabled}
184
+ />
185
+ ))}
186
+ </Picker>
187
+ ) : null}
188
+
189
+ {/* Web: Picker renders as native <select> */}
190
+ {isWeb ? (
191
+ <Picker
192
+ selectedValue={value ?? ''}
193
+ onValueChange={(val) => {
194
+ if (val !== '') {
195
+ onValueChange?.(val as string)
196
+ }
197
+ }}
198
+ enabled={!disabled}
95
199
  style={[
96
- styles.triggerText,
97
- { color: selected ? colors.foreground : colors.mutedForeground },
200
+ styles.webPicker,
201
+ {
202
+ borderColor: error ? colors.destructive : colors.border,
203
+ color: selected ? colors.foreground : colors.mutedForeground,
204
+ backgroundColor: colors.background,
205
+ opacity: disabled ? 0.45 : 1,
206
+ },
98
207
  ]}
99
- numberOfLines={1}
100
208
  >
101
- {selected?.label ?? placeholder}
102
- </Text>
103
- <Text style={[styles.chevron, { color: colors.mutedForeground }]}>▾</Text>
104
- </TouchableOpacity>
105
- </Animated.View>
209
+ <Picker.Item label={placeholder} value="" enabled={false} />
210
+ {options.map((o) => (
211
+ <Picker.Item
212
+ key={o.value}
213
+ label={o.label}
214
+ value={o.value}
215
+ enabled={!o.disabled}
216
+ />
217
+ ))}
218
+ </Picker>
219
+ ) : null}
106
220
 
107
221
  {error ? (
108
222
  <Text style={[styles.helperText, { color: colors.destructive }]}>{error}</Text>
109
223
  ) : null}
110
-
111
- <BottomSheetModal
112
- ref={bottomSheetRef}
113
- enableDynamicSizing
114
- enablePanDownToClose
115
- backdropComponent={renderBackdrop}
116
- backgroundStyle={[styles.sheetBackground, { backgroundColor: colors.card }]}
117
- handleIndicatorStyle={[styles.sheetHandle, { backgroundColor: colors.border }]}
118
- >
119
- <BottomSheetView style={styles.sheetContent}>
120
- {label ? (
121
- <Text style={[styles.sheetTitle, { color: colors.foreground }]}>{label}</Text>
122
- ) : null}
123
- {options.map((item) => {
124
- const isSelected = item.value === value
125
- return (
126
- <TouchableOpacity
127
- key={item.value}
128
- style={[
129
- styles.option,
130
- isSelected && { backgroundColor: colors.accent },
131
- item.disabled && styles.disabledOption,
132
- ]}
133
- onPress={() => {
134
- if (!item.disabled) {
135
- Haptics.selectionAsync()
136
- onValueChange?.(item.value)
137
- bottomSheetRef.current?.dismiss()
138
- }
139
- }}
140
- activeOpacity={0.7}
141
- touchSoundDisabled={true}
142
- >
143
- <Text
144
- style={[
145
- styles.optionText,
146
- { color: item.disabled ? colors.mutedForeground : colors.foreground },
147
- isSelected && { fontWeight: '500' },
148
- ]}
149
- >
150
- {item.label}
151
- </Text>
152
- {isSelected ? (
153
- <Text style={[styles.checkmark, { color: colors.primary }]}>✓</Text>
154
- ) : null}
155
- </TouchableOpacity>
156
- )
157
- })}
158
- </BottomSheetView>
159
- </BottomSheetModal>
160
224
  </View>
161
225
  )
162
226
  }
163
227
 
164
228
  const styles = StyleSheet.create({
165
229
  container: {
166
- gap: 6,
230
+ gap: 8,
167
231
  },
168
232
  label: {
169
233
  fontSize: 15,
170
234
  fontWeight: '500',
171
- marginBottom: 2,
172
235
  },
173
236
  trigger: {
174
237
  flexDirection: 'row',
175
238
  alignItems: 'center',
176
239
  justifyContent: 'space-between',
177
240
  borderWidth: 1.5,
178
- borderRadius: 14,
179
- paddingHorizontal: 20,
180
- paddingVertical: 16,
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,
181
249
  },
182
250
  triggerText: {
183
251
  fontSize: 17,
184
252
  flex: 1,
185
253
  },
186
254
  chevron: {
187
- fontSize: 16,
188
255
  marginLeft: 8,
189
256
  },
190
257
  helperText: {
191
258
  fontSize: 13,
192
259
  },
193
- sheetBackground: {
194
- borderTopLeftRadius: 24,
195
- borderTopRightRadius: 24,
196
- },
197
- sheetHandle: {
198
- width: 36,
199
- height: 4,
200
- borderRadius: 2,
201
- },
202
- sheetContent: {
203
- paddingHorizontal: 20,
204
- paddingBottom: 36,
260
+ iosBackdrop: {
261
+ flex: 1,
262
+ backgroundColor: 'rgba(0,0,0,0.4)',
205
263
  },
206
- sheetTitle: {
207
- fontSize: 17,
208
- fontWeight: '600',
209
- paddingVertical: 16,
210
- paddingHorizontal: 4,
264
+ iosSheet: {
265
+ borderTopLeftRadius: 16,
266
+ borderTopRightRadius: 16,
267
+ paddingBottom: 32,
211
268
  },
212
- option: {
269
+ iosToolbar: {
213
270
  flexDirection: 'row',
214
271
  alignItems: 'center',
215
272
  justifyContent: 'space-between',
216
273
  paddingHorizontal: 16,
217
- paddingVertical: 16,
218
- borderRadius: 12,
274
+ paddingVertical: 12,
275
+ borderBottomWidth: 1,
219
276
  },
220
- optionText: {
277
+ iosToolbarTitle: {
221
278
  fontSize: 17,
222
- flex: 1,
279
+ fontWeight: '600',
223
280
  },
224
- disabledOption: {
225
- opacity: 0.45,
281
+ iosDoneBtn: {
282
+ padding: 4,
226
283
  },
227
- checkmark: {
228
- fontSize: 16,
284
+ iosDoneBtnText: {
285
+ fontSize: 17,
229
286
  fontWeight: '600',
230
- marginLeft: 8,
287
+ },
288
+ androidHiddenPicker: {
289
+ height: 0,
290
+ opacity: 0,
291
+ position: 'absolute',
292
+ },
293
+ webPicker: {
294
+ borderWidth: 1.5,
295
+ borderRadius: 8,
296
+ paddingHorizontal: 16,
297
+ paddingVertical: 14,
298
+ fontSize: 17,
231
299
  },
232
300
  })
@@ -1,19 +1,20 @@
1
- import React, { useRef, useState } from 'react'
2
- import { View, PanResponder, StyleSheet, LayoutChangeEvent, ViewStyle } from 'react-native'
1
+ import React, { useRef } from 'react'
2
+ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
+ import RNSlider from '@react-native-community/slider'
3
4
  import * as Haptics from 'expo-haptics'
4
5
  import { useTheme } from '../../theme'
5
6
 
6
7
  export interface SliderProps {
7
- /** Current value. Controlled when provided; falls back to internal state otherwise. */
8
8
  value?: number
9
9
  minimumValue?: number
10
10
  maximumValue?: number
11
- /** Snap interval. `0` (default) means continuous (no snapping). */
12
11
  step?: number
13
- /** Called on every move while dragging. */
14
12
  onValueChange?: (value: number) => void
15
- /** Called once when the user releases the thumb. */
16
13
  onSlidingComplete?: (value: number) => void
14
+ label?: string
15
+ showValue?: boolean
16
+ formatValue?: (value: number) => string
17
+ accessibilityLabel?: string
17
18
  disabled?: boolean
18
19
  style?: ViewStyle
19
20
  }
@@ -25,119 +26,82 @@ export function Slider({
25
26
  step = 0,
26
27
  onValueChange,
27
28
  onSlidingComplete,
29
+ label,
30
+ showValue = false,
31
+ formatValue = (v: number) => v.toFixed(2),
32
+ accessibilityLabel,
28
33
  disabled,
29
34
  style,
30
35
  }: SliderProps) {
31
36
  const { colors } = useTheme()
32
- const trackWidth = useRef(0)
33
37
  const lastSteppedValue = useRef(value)
34
- const [internalValue, setInternalValue] = useState(value)
35
- const currentValue = value ?? internalValue
36
38
 
37
- const clamp = (v: number) => Math.min(Math.max(v, minimumValue), maximumValue)
38
-
39
- const snapToStep = (v: number) => {
40
- if (!step) return v
41
- return Math.round((v - minimumValue) / step) * step + minimumValue
42
- }
43
-
44
- const xToValue = (x: number): number => {
45
- const ratio = Math.min(Math.max(x / trackWidth.current, 0), 1)
46
- const raw = ratio * (maximumValue - minimumValue) + minimumValue
47
- return clamp(snapToStep(raw))
39
+ const handleValueChange = (v: number) => {
40
+ if (step && v !== lastSteppedValue.current) {
41
+ lastSteppedValue.current = v
42
+ Haptics.selectionAsync()
43
+ }
44
+ onValueChange?.(v)
48
45
  }
49
46
 
50
- const panResponder = useRef(
51
- PanResponder.create({
52
- onStartShouldSetPanResponder: () => !disabled,
53
- onMoveShouldSetPanResponder: () => !disabled,
54
- onPanResponderGrant: (e) => {
55
- const x = e.nativeEvent.locationX
56
- const newValue = xToValue(x)
57
- setInternalValue(newValue)
58
- onValueChange?.(newValue)
59
- },
60
- onPanResponderMove: (e) => {
61
- const x = e.nativeEvent.locationX
62
- const newValue = xToValue(x)
63
- if (newValue !== lastSteppedValue.current) {
64
- lastSteppedValue.current = newValue
65
- Haptics.selectionAsync()
66
- }
67
- setInternalValue(newValue)
68
- onValueChange?.(newValue)
69
- },
70
- onPanResponderRelease: (e) => {
71
- const x = e.nativeEvent.locationX
72
- const newValue = xToValue(x)
73
- setInternalValue(newValue)
74
- onSlidingComplete?.(newValue)
75
- },
76
- })
77
- ).current
78
-
79
- const onLayout = (e: LayoutChangeEvent) => {
80
- trackWidth.current = e.nativeEvent.layout.width
81
- }
82
-
83
- const percent = ((currentValue - minimumValue) / (maximumValue - minimumValue)) * 100
84
-
85
47
  return (
86
- <View
87
- style={[styles.container, disabled && styles.disabled, style]}
88
- {...panResponder.panHandlers}
89
- onLayout={onLayout}
90
- >
91
- <View style={[styles.track, { backgroundColor: colors.muted }]}>
92
- <View
93
- style={[styles.range, { width: `${percent}%` as any, backgroundColor: colors.primary }]}
48
+ <View style={[styles.wrapper, style]} accessibilityLabel={accessibilityLabel}>
49
+ {label || showValue ? (
50
+ <View style={styles.header}>
51
+ {label ? (
52
+ <Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>
53
+ {label}
54
+ </Text>
55
+ ) : null}
56
+ {showValue ? (
57
+ <Text style={[styles.valueText, { color: colors.mutedForeground }]} allowFontScaling={true}>
58
+ {formatValue(value)}
59
+ </Text>
60
+ ) : null}
61
+ </View>
62
+ ) : null}
63
+ <View style={disabled ? styles.disabled : undefined}>
64
+ <RNSlider
65
+ value={value}
66
+ minimumValue={minimumValue}
67
+ maximumValue={maximumValue}
68
+ step={step || 0}
69
+ disabled={disabled}
70
+ onValueChange={handleValueChange}
71
+ onSlidingComplete={onSlidingComplete}
72
+ minimumTrackTintColor={colors.primary}
73
+ maximumTrackTintColor={colors.muted}
74
+ thumbTintColor={colors.primary}
75
+ style={styles.slider}
76
+ accessibilityLabel={accessibilityLabel}
94
77
  />
95
78
  </View>
96
- <View
97
- style={[
98
- styles.thumb,
99
- {
100
- left: `${percent}%` as any,
101
- backgroundColor: colors.primary,
102
- borderColor: colors.background,
103
- transform: [{ translateX: -14 }],
104
- },
105
- ]}
106
- pointerEvents="none"
107
- />
108
79
  </View>
109
80
  )
110
81
  }
111
82
 
112
83
  const styles = StyleSheet.create({
113
- container: {
114
- height: 32,
115
- justifyContent: 'center',
116
- position: 'relative',
84
+ wrapper: {
85
+ gap: 8,
117
86
  },
118
- disabled: {
119
- opacity: 0.45,
87
+ header: {
88
+ flexDirection: 'row',
89
+ justifyContent: 'space-between',
90
+ alignItems: 'center',
120
91
  },
121
- track: {
122
- height: 6,
123
- borderRadius: 3,
124
- overflow: 'hidden',
125
- width: '100%',
92
+ label: {
93
+ fontSize: 15,
94
+ fontWeight: '500',
126
95
  },
127
- range: {
128
- height: '100%',
129
- borderRadius: 3,
96
+ valueText: {
97
+ fontSize: 14,
98
+ fontWeight: '500',
130
99
  },
131
- thumb: {
132
- position: 'absolute',
133
- width: 28,
134
- height: 28,
135
- borderRadius: 14,
136
- borderWidth: 2,
137
- shadowColor: '#000',
138
- shadowOffset: { width: 0, height: 1 },
139
- shadowOpacity: 0.2,
140
- shadowRadius: 2,
141
- elevation: 2,
100
+ slider: {
101
+ width: '100%',
102
+ height: 40,
103
+ },
104
+ disabled: {
105
+ opacity: 0.45,
142
106
  },
143
107
  })
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
- import { TouchableOpacity, Animated, StyleSheet, ViewStyle, Platform } from 'react-native'
2
+ import { TouchableOpacity, Animated, StyleSheet, ViewStyle, Platform, View } from 'react-native'
3
3
 
4
4
  const nativeDriver = Platform.OS !== 'web'
5
5
  import * as Haptics from 'expo-haptics'
@@ -44,25 +44,27 @@ export function Switch({ checked = false, onCheckedChange, disabled, style }: Sw
44
44
  })
45
45
 
46
46
  return (
47
- <TouchableOpacity
48
- onPress={() => {
49
- Haptics.selectionAsync()
50
- onCheckedChange?.(!checked)
51
- }}
52
- disabled={disabled}
53
- activeOpacity={0.8}
54
- touchSoundDisabled={true}
55
- style={[styles.wrapper, { opacity: disabled ? 0.45 : 1 }, style]}
56
- >
57
- <Animated.View style={[styles.track, { backgroundColor: trackColor }]}>
58
- <Animated.View
59
- style={[
60
- styles.thumb,
61
- { backgroundColor: colors.primaryForeground, transform: [{ translateX }] },
62
- ]}
63
- />
64
- </Animated.View>
65
- </TouchableOpacity>
47
+ <View style={[{ opacity: disabled ? 0.45 : 1 }, style]}>
48
+ <TouchableOpacity
49
+ onPress={() => {
50
+ Haptics.selectionAsync()
51
+ onCheckedChange?.(!checked)
52
+ }}
53
+ disabled={disabled}
54
+ activeOpacity={0.8}
55
+ touchSoundDisabled={true}
56
+ style={styles.wrapper}
57
+ >
58
+ <Animated.View style={[styles.track, { backgroundColor: trackColor }]}>
59
+ <Animated.View
60
+ style={[
61
+ styles.thumb,
62
+ { backgroundColor: colors.primaryForeground, transform: [{ translateX }] },
63
+ ]}
64
+ />
65
+ </Animated.View>
66
+ </TouchableOpacity>
67
+ </View>
66
68
  )
67
69
  }
68
70