react-native-varia 0.2.4 → 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 +21 -6
  4. package/lib/components/Checkbox.tsx +22 -8
  5. package/lib/components/CircleProgress.tsx +8 -0
  6. package/lib/components/Divider.tsx +11 -4
  7. package/lib/components/Drawer.tsx +34 -25
  8. package/lib/components/Field.tsx +4 -0
  9. package/lib/components/GradientBackground.tsx +3 -0
  10. package/lib/components/GradientText.tsx +37 -11
  11. package/lib/components/IconWrapper.tsx +3 -2
  12. package/lib/components/Input.tsx +47 -20
  13. package/lib/components/Link.tsx +4 -0
  14. package/lib/components/Modal.tsx +21 -14
  15. package/lib/components/NumberInput.tsx +27 -5
  16. package/lib/components/RadioGroup.tsx +19 -6
  17. package/lib/components/ReText.tsx +4 -1
  18. package/lib/components/Select.tsx +20 -0
  19. package/lib/components/Slider.tsx +244 -134
  20. package/lib/components/Slideshow.tsx +19 -3
  21. package/lib/components/Spinner.tsx +12 -2
  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 -79
  26. package/lib/theme/Accordion.tsx +184 -0
  27. package/lib/theme/Button.recipe.tsx +24 -7
  28. package/lib/theme/{SlidingDrawer.recipe.tsx → Drawer.recipe.tsx} +4 -6
  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 +108 -141
  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/mixins.ts +0 -4
  39. package/lib/varia/types.ts +11 -0
  40. package/lib/varia/utils.ts +172 -14
  41. package/package.json +1 -1
  42. package/lib/components/OldSlider.tsx +0 -327
  43. package/lib/components/SlidingDrawer.tsx +0 -301
@@ -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
+ }