react-native-varia 0.3.0 → 0.4.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 (43) hide show
  1. package/lib/components/Accordion.tsx +61 -21
  2. package/lib/components/Badge.tsx +9 -0
  3. package/lib/components/Button.tsx +19 -8
  4. package/lib/components/Checkbox.tsx +21 -12
  5. package/lib/components/CircleProgress.tsx +8 -0
  6. package/lib/components/Divider.tsx +6 -0
  7. package/lib/components/Drawer.tsx +16 -4
  8. package/lib/components/Field.tsx +4 -0
  9. package/lib/components/GradientBackground.tsx +3 -0
  10. package/lib/components/GradientText.tsx +27 -14
  11. package/lib/components/IconWrapper.tsx +3 -2
  12. package/lib/components/Input.tsx +47 -21
  13. package/lib/components/Link.tsx +4 -0
  14. package/lib/components/Modal.tsx +18 -5
  15. package/lib/components/NumberInput.tsx +27 -5
  16. package/lib/components/RadioGroup.tsx +16 -5
  17. package/lib/components/ReText.tsx +4 -1
  18. package/lib/components/Select.tsx +20 -0
  19. package/lib/components/Slider.tsx +59 -23
  20. package/lib/components/Slideshow.tsx +19 -3
  21. package/lib/components/Spinner.tsx +9 -3
  22. package/lib/components/Switch.tsx +57 -26
  23. package/lib/components/Text.tsx +3 -0
  24. package/lib/components/Toast.tsx +110 -36
  25. package/lib/patterns/index.tsx +299 -90
  26. package/lib/theme/Accordion.tsx +184 -0
  27. package/lib/theme/Button.recipe.tsx +24 -7
  28. package/lib/theme/Drawer.recipe.tsx +2 -4
  29. package/lib/theme/Field.recipe.tsx +45 -15
  30. package/lib/theme/GradientText.recipe.tsx +103 -34
  31. package/lib/theme/Input.recipe.tsx +14 -6
  32. package/lib/theme/Select.recipe.tsx +3 -0
  33. package/lib/theme/Slider.recipe.tsx +86 -150
  34. package/lib/theme/Spinner.recipe.tsx +4 -0
  35. package/lib/theme/Switch.recipe.tsx +19 -0
  36. package/lib/theme/Text.recipe.tsx +63 -12
  37. package/lib/theme/Toast.recipe.tsx +40 -7
  38. package/lib/varia/types.ts +3 -0
  39. package/lib/varia/utils.ts +110 -18
  40. package/package.json +1 -1
  41. package/lib/components/OldSlider.tsx +0 -327
  42. package/lib/components/SlidingDrawer.tsx +0 -301
  43. package/lib/patterns/newPatterns.tsx +0 -285
@@ -18,6 +18,7 @@ export type SlideshowRef = {
18
18
  handlePrev: () => void | null
19
19
  }
20
20
 
21
+ const AnimatedView = Animated.createAnimatedComponent(View)
21
22
  type SlideshowVariants = UnistylesVariants<typeof SlideshowStyles>
22
23
 
23
24
  type Axis = 'x' | 'y'
@@ -174,7 +175,7 @@ const Slideshow = ({
174
175
  setContainerWidth(e.nativeEvent.layout.width)
175
176
  setContainerHeight(e.nativeEvent.layout.height)
176
177
  }}>
177
- <Animated.View
178
+ <AnimatedView
178
179
  style={[
179
180
  styles.slidesContainer(`${NUM_PAGES * 100}%`, isHorizontal),
180
181
  animatedStyle,
@@ -189,7 +190,7 @@ const Slideshow = ({
189
190
  {Slide}
190
191
  </View>
191
192
  ))}
192
- </Animated.View>
193
+ </AnimatedView>
193
194
  </View>
194
195
  </GestureDetector>
195
196
  </View>
@@ -201,15 +202,24 @@ export default Slideshow
201
202
  const styles = StyleSheet.create({
202
203
  flex: flex => ({
203
204
  flex,
205
+ flexBasis: 'auto',
204
206
  flexDirection: 'row',
207
+ alignSelf: 'stretch',
208
+ _web: {
209
+ _classNames: 'slideshow-container-flex-base',
210
+ },
205
211
  }),
206
212
 
207
213
  container: () => ({
214
+ flexBasis: 'auto',
208
215
  width: '100%',
209
- height: '100%',
216
+ maxHeight: '100%',
210
217
  overflow: 'hidden',
211
218
  justifyContent: 'flex-start',
212
219
  alignItems: 'flex-start',
220
+ _web: {
221
+ _classNames: 'slideshow-container-base',
222
+ },
213
223
  }),
214
224
 
215
225
  slidesContainer: (size, isHorizontal) => ({
@@ -217,6 +227,9 @@ const styles = StyleSheet.create({
217
227
  width: isHorizontal ? size : '100%',
218
228
  minHeight: isHorizontal ? '100%' : size,
219
229
  flex: 1,
230
+ _web: {
231
+ _classNames: 'slideshow-container-slides-container-base',
232
+ },
220
233
  }),
221
234
 
222
235
  slide: (size, isHorizontal) => ({
@@ -225,5 +238,8 @@ const styles = StyleSheet.create({
225
238
  height: isHorizontal ? '100%' : size,
226
239
  justifyContent: 'center',
227
240
  alignItems: 'center',
241
+ _web: {
242
+ _classNames: 'slideshow-container-slide-base',
243
+ },
228
244
  }),
229
245
  })
@@ -34,17 +34,23 @@ const BaseSpinner = ({
34
34
  ...props
35
35
  }: SpinnerProps) => {
36
36
  const resolvedColor = resolveColor(color, colors, colorPalette)
37
-
37
+
38
38
  SpinnerStyles.useVariants({
39
39
  size,
40
40
  })
41
41
 
42
42
  // console.log('color', color)
43
43
 
44
- const resolvedSize = getVariantValue(SpinnerStyles.base, 'size', size, 'width')
45
-
44
+ const resolvedSize = getVariantValue(
45
+ SpinnerStyles.base,
46
+ 'size',
47
+ size,
48
+ 'width',
49
+ )
50
+
46
51
  return (
47
52
  <ActivityIndicator
53
+ testID="spinner"
48
54
  color={resolvedColor}
49
55
  size={resolvedSize}
50
56
  {...props}
@@ -1,4 +1,4 @@
1
- import {TouchableWithoutFeedback, View} from 'react-native'
1
+ import {Platform, TouchableWithoutFeedback, View} from 'react-native'
2
2
  import Animated, {
3
3
  useAnimatedStyle,
4
4
  useSharedValue,
@@ -9,13 +9,15 @@ import {StyleSheet, UnistylesVariants} from 'react-native-unistyles'
9
9
  import {useAnimatedVariantColor} from 'react-native-unistyles/reanimated'
10
10
  import {ReactNode, useLayoutEffect, useRef} from 'react'
11
11
  import {SwitchTokens, SwitchStyles} from '../theme/Switch.recipe'
12
- import {PalettesWithNestedKeys} from '../style/varia/types'
12
+ import {AlignSelf, Flex, PalettesWithNestedKeys} from '../style/varia/types'
13
+ import {getVariantValue} from '../style/varia/utils'
13
14
 
14
15
  const animations = {
15
16
  withSpring,
16
17
  withTiming,
17
18
  }
18
19
 
20
+ const AnimatedView = Animated.createAnimatedComponent(View)
19
21
  type AnimationType = 'withSpring' | 'withTiming'
20
22
 
21
23
  type SwitchVariants = UnistylesVariants<typeof SwitchStyles>
@@ -25,9 +27,10 @@ type SwitchProps = SwitchVariants & {
25
27
  value?: boolean
26
28
  animation?: keyof typeof SwitchTokens.variants.animation
27
29
  onValueChange?: (value: boolean) => void
28
- flex?: number
29
- enabledIcon: ReactNode
30
- disabledIcon: ReactNode
30
+ flex?: Flex
31
+ enabledIcon?: ReactNode
32
+ disabledIcon?: ReactNode
33
+ alignSelf?: AlignSelf
31
34
  }
32
35
 
33
36
  const Switch = ({
@@ -39,7 +42,8 @@ const Switch = ({
39
42
  onValueChange = () => {},
40
43
  variant = SwitchTokens.defaultProps.variant,
41
44
  size = SwitchTokens.defaultProps.size,
42
- flex = 1,
45
+ flex = 0,
46
+ alignSelf = 'flex-start',
43
47
  }: SwitchProps) => {
44
48
  const animatedRef = useRef<View>(null)
45
49
 
@@ -62,15 +66,20 @@ const Switch = ({
62
66
  mass: 1,
63
67
  },
64
68
  }
65
-
66
- const color = useAnimatedVariantColor(
67
- SwitchStyles.container(colorPalette),
68
- 'backgroundColor',
69
- )
70
- const thumbColor = useAnimatedVariantColor(
71
- SwitchStyles.thumb(colorPalette),
72
- 'backgroundColor',
73
- )
69
+ const color =
70
+ Platform.OS === 'web'
71
+ ? {value: 'blue'}
72
+ : useAnimatedVariantColor(
73
+ SwitchStyles.container(colorPalette),
74
+ 'backgroundColor',
75
+ )
76
+ const thumbColor =
77
+ Platform.OS === 'web'
78
+ ? {value: 'white'}
79
+ : useAnimatedVariantColor(
80
+ SwitchStyles.thumb(colorPalette),
81
+ 'backgroundColor',
82
+ )
74
83
  const thumbEnabledPosition = useSharedValue(0)
75
84
  const animatedValue = useSharedValue(0)
76
85
  const containerWidth = useSharedValue(0)
@@ -78,10 +87,24 @@ const Switch = ({
78
87
  useLayoutEffect(() => {
79
88
  animatedRef.current?.measure((x, y, width) => {
80
89
  containerWidth.value = width
81
- const thumbWidth = SwitchStyles.thumb(colorPalette).width
82
- const padding = SwitchStyles.container(colorPalette).padding || 0
83
- const trackBorderWidth =
84
- SwitchStyles.container(colorPalette).borderWidth || 0
90
+ // const thumbWidth = SwitchStyles.thumb(colorPalette).width
91
+ const thumbWidth = getVariantValue(
92
+ SwitchStyles.thumb(colorPalette),
93
+ 'size',
94
+ size,
95
+ 'width',
96
+ )
97
+ // const padding = SwitchStyles.container(colorPalette).padding || 0
98
+ const padding =
99
+ getVariantValue(
100
+ SwitchStyles.container(colorPalette),
101
+ 'size',
102
+ size,
103
+ 'padding',
104
+ ) || 0
105
+ // const trackBorderWidth =
106
+ // SwitchStyles.container(colorPalette).borderWidth || 0
107
+ const trackBorderWidth = 1
85
108
  thumbEnabledPosition.value =
86
109
  width - thumbWidth - trackBorderWidth * 2 - padding * 2
87
110
  })
@@ -133,14 +156,14 @@ const Switch = ({
133
156
  },
134
157
  )
135
158
  }}>
136
- <Animated.View
159
+ <AnimatedView
137
160
  ref={animatedRef}
138
161
  style={[
139
- styles.container(flex),
162
+ styles.container(flex, alignSelf),
140
163
  animatedStyle,
141
164
  SwitchStyles.container(colorPalette),
142
165
  ]}>
143
- <Animated.View
166
+ <AnimatedView
144
167
  style={[
145
168
  styles.thumb,
146
169
  SwitchStyles.thumb(colorPalette),
@@ -148,8 +171,8 @@ const Switch = ({
148
171
  circleTranslationStyle,
149
172
  ]}>
150
173
  {Icon && Icon}
151
- </Animated.View>
152
- </Animated.View>
174
+ </AnimatedView>
175
+ </AnimatedView>
153
176
  </TouchableWithoutFeedback>
154
177
  )
155
178
  }
@@ -157,17 +180,25 @@ const Switch = ({
157
180
  export default Switch
158
181
 
159
182
  const styles = StyleSheet.create({
160
- container: (flex: number) => ({
183
+ container: (flex: Flex, alignSelf: AlignSelf) => ({
161
184
  flexGrow: flex,
185
+ flexBasis: 'auto',
186
+ alignSelf,
162
187
  flexShrink: 0,
163
- alignSelf: 'flex-start',
188
+ // alignSelf: 'flex-start',
164
189
  flexDirection: 'row',
165
190
  alignItems: 'center',
166
191
  margin: 0,
192
+ _web: {
193
+ _classNames: 'switch-container-base',
194
+ },
167
195
  }),
168
196
  thumb: {
169
197
  borderRadius: 9999,
170
198
  justifyContent: 'center',
171
199
  alignItems: 'center',
200
+ _web: {
201
+ _classNames: 'switch-thumb-base',
202
+ },
172
203
  },
173
204
  })
@@ -54,6 +54,9 @@ const styles = StyleSheet.create(theme => ({
54
54
  color: extractThemeColor(color, theme),
55
55
  }),
56
56
  ...(fontSize && {fontSize}),
57
+ _web: {
58
+ _classNames: 'text-base',
59
+ },
57
60
  }),
58
61
  }))
59
62
 
@@ -1,23 +1,28 @@
1
- import React, {useEffect} from 'react'
2
- import {StyleSheet} from 'react-native'
1
+ import React, {useEffect, useState} from 'react'
2
+ import {StyleSheet} from 'react-native-unistyles'
3
3
  import Animated, {
4
4
  useSharedValue,
5
5
  useAnimatedStyle,
6
+ useAnimatedRef,
7
+ measure,
6
8
  withTiming,
7
9
  Easing,
8
10
  } from 'react-native-reanimated'
9
- import {scheduleOnRN} from 'react-native-worklets'
11
+ import {scheduleOnRN, scheduleOnUI} from 'react-native-worklets'
10
12
  import {ToastStyles, ToastDefaultVariants} from '../theme/Toast.recipe'
11
13
  import {UnistylesVariants} from 'react-native-unistyles'
12
14
  import {PalettesWithNestedKeys} from '../style/varia/types'
13
15
  import Text from './Text'
16
+ import {AnimatedHStack, VStack} from '../patterns/newPatterns'
14
17
 
15
18
  type ToastVariants = UnistylesVariants<typeof ToastStyles>
16
- type ToastProps = ToastVariants & {
19
+
20
+ export type ToastProps = ToastVariants & {
17
21
  colorPalette?: PalettesWithNestedKeys
18
22
  message: string
19
23
  duration?: number
20
24
  onClose?: () => void
25
+ position?: 'top' | 'bottom'
21
26
  }
22
27
 
23
28
  const Toast: React.FC<ToastProps> = ({
@@ -27,13 +32,13 @@ const Toast: React.FC<ToastProps> = ({
27
32
  message,
28
33
  duration = 5000,
29
34
  onClose,
35
+ position = 'bottom',
30
36
  }) => {
31
- ToastStyles.useVariants({
32
- variant,
33
- size,
34
- })
37
+ ToastStyles.useVariants({variant, size})
38
+
39
+ const animatedRef = useAnimatedRef<Animated.View>()
40
+ const translateY = useSharedValue(0)
35
41
  const opacity = useSharedValue(0)
36
- const translateY = useSharedValue(50) // Aparece desde abajo
37
42
 
38
43
  const animatedStyle = useAnimatedStyle(() => ({
39
44
  opacity: opacity.value,
@@ -41,49 +46,118 @@ const Toast: React.FC<ToastProps> = ({
41
46
  }))
42
47
 
43
48
  useEffect(() => {
44
- // Animación de entrada
45
- opacity.value = withTiming(1, {
46
- duration: 200,
47
- easing: Easing.out(Easing.ease),
48
- })
49
- translateY.value = withTiming(0, {
50
- duration: 200,
51
- easing: Easing.out(Easing.ease),
52
- })
49
+ scheduleOnUI(() => {
50
+ 'worklet'
51
+ const m = measure(animatedRef)
52
+ if (m === null) return
53
+ const height = m.height
54
+
55
+ translateY.value = position === 'bottom' ? height : -height
56
+ opacity.value = 0
53
57
 
54
- // Animación de salida
55
- const timeout = setTimeout(() => {
56
- opacity.value = withTiming(0, {duration: 300})
57
- translateY.value = withTiming(50, {duration: 300}, () => {
58
- if (onClose) scheduleOnRN(onClose)
58
+ translateY.value = withTiming(0, {
59
+ duration: 200,
60
+ easing: Easing.out(Easing.ease),
61
+ })
62
+ opacity.value = withTiming(1, {
63
+ duration: 200,
64
+ easing: Easing.out(Easing.ease),
59
65
  })
60
- }, duration)
61
66
 
62
- return () => clearTimeout(timeout)
67
+ const timeout = setTimeout(() => {
68
+ opacity.value = withTiming(0, {duration: 300})
69
+ translateY.value = withTiming(
70
+ position === 'bottom' ? height : -height,
71
+ {duration: 300},
72
+ () => {
73
+ if (onClose) {
74
+ scheduleOnRN(onClose)
75
+ }
76
+ },
77
+ )
78
+ }, duration)
79
+ })
63
80
  }, [])
64
81
 
65
82
  return (
66
- <Animated.View
83
+ <VStack
67
84
  style={[
85
+ {
86
+ ...StyleSheet.absoluteFill,
87
+ justifyContent:
88
+ position === 'bottom' || !position ? 'flex-end' : 'flex-start',
89
+ },
68
90
  styles.container,
69
- animatedStyle,
70
- ToastStyles.container(colorPalette),
91
+ ToastStyles.root,
71
92
  ]}>
72
- <Text style={ToastStyles.text(colorPalette)}>{message}</Text>
73
- </Animated.View>
93
+ <AnimatedHStack
94
+ ref={animatedRef}
95
+ style={[animatedStyle, ToastStyles.container(colorPalette)]}>
96
+ <Text style={ToastStyles.text(colorPalette)}>{message}</Text>
97
+ </AnimatedHStack>
98
+ </VStack>
74
99
  )
75
100
  }
76
101
 
77
102
  const styles = StyleSheet.create({
78
103
  container: {
79
- position: 'absolute',
80
- bottom: 50,
81
- // left: 20,
82
- // right: 20,
83
- padding: 15,
84
- borderRadius: 8,
85
104
  alignItems: 'center',
105
+ overflow: 'hidden',
106
+ zIndex: 100,
107
+ pointerEvents: 'box-none',
108
+ _web: {
109
+ pointerEvents: 'none',
110
+ _classNames: 'toast-container-base',
111
+ },
86
112
  },
87
113
  })
88
114
 
89
115
  export default Toast
116
+
117
+ type ToastItem = ToastProps & {id: number}
118
+
119
+ let nextId = 0
120
+ let enqueueToastInternal: ((opts: ToastProps) => number | void) | null = null
121
+
122
+ export function showToast(opts: ToastProps): number | void {
123
+ if (enqueueToastInternal) {
124
+ return enqueueToastInternal(opts)
125
+ } else {
126
+ console.warn('ToastController not mounted — no toast shown')
127
+ }
128
+ }
129
+
130
+ export const ToastController: React.FC = () => {
131
+ const [currentToast, setCurrentToast] = useState<ToastItem | null>(null)
132
+ const [queue, setQueue] = useState<ToastItem[]>([])
133
+
134
+ useEffect(() => {
135
+ enqueueToastInternal = (opts: ToastProps) => {
136
+ const id = nextId++
137
+ const toastItem = {...opts, id}
138
+
139
+ setQueue(prev => [...prev, toastItem])
140
+ return id
141
+ }
142
+
143
+ return () => {
144
+ enqueueToastInternal = null
145
+ }
146
+ }, [])
147
+
148
+ const handleClose = () => {
149
+ setCurrentToast(null)
150
+ }
151
+
152
+ useEffect(() => {
153
+ if (!currentToast && queue.length > 0) {
154
+ const [nextToast, ...rest] = queue
155
+ setCurrentToast(nextToast)
156
+ setQueue(rest)
157
+ }
158
+ }, [currentToast, queue])
159
+
160
+ if (!currentToast) return null
161
+
162
+ return <Toast key={currentToast.id} {...currentToast} onClose={handleClose} />
163
+ }