react-native-varia 0.2.1 → 0.2.3
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/README.md +183 -17
- package/bin/cli.js +316 -129
- package/lib/components/Badge.tsx +27 -21
- package/lib/components/Button.tsx +12 -13
- package/lib/components/Checkbox.tsx +7 -11
- package/lib/components/CircleProgress.tsx +20 -22
- package/lib/components/Drawer.tsx +539 -0
- package/lib/components/Field.tsx +2 -6
- package/lib/components/Input.tsx +6 -2
- package/lib/components/Link.tsx +1 -1
- package/lib/components/Modal.tsx +2 -4
- package/lib/components/NumberInput.tsx +35 -10
- package/lib/components/ReText.tsx +1 -1
- package/lib/components/Select.tsx +1 -1
- package/lib/components/Slider.tsx +9 -11
- package/lib/components/Slideshow.tsx +3 -6
- package/lib/components/SlidingDrawer.tsx +61 -17
- package/lib/components/Spinner.tsx +2 -3
- package/lib/components/Switch.tsx +2 -6
- package/lib/icons/Minus.tsx +24 -0
- package/lib/icons/Plus.tsx +23 -0
- package/lib/patterns/index.tsx +1 -2
- package/lib/theme/NumberInput.recipe.tsx +1 -1
- package/package.json +3 -2
- package/lib/components/NewSelect.tsx +0 -202
- package/lib/components/SelectOld.tsx +0 -153
- package/lib/components/Switchold.tsx +0 -174
- package/lib/components/index.tsx +0 -83
- package/lib/components/layoutTest.tsx +0 -74
- package/lib/mixins.ts +0 -180
- package/lib/types.ts +0 -70
- package/lib/utils.ts +0 -80
|
@@ -31,10 +31,12 @@ function Checkbox({
|
|
|
31
31
|
|
|
32
32
|
const isChecked = checked
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const checkStyle = CheckboxStyles.check(colorPalette) as {
|
|
35
|
+
width: string
|
|
36
|
+
color: string
|
|
37
|
+
}
|
|
38
|
+
const checkSize = checkStyle.width
|
|
39
|
+
const checkColor = checkStyle.color
|
|
38
40
|
|
|
39
41
|
return (
|
|
40
42
|
<Pressable
|
|
@@ -67,13 +69,7 @@ export {Checkbox}
|
|
|
67
69
|
|
|
68
70
|
const Check = ({size, color}: {size: string; color: string}) => {
|
|
69
71
|
return (
|
|
70
|
-
<Svg
|
|
71
|
-
width={size}
|
|
72
|
-
height={size}
|
|
73
|
-
viewBox="0 0 16 13"
|
|
74
|
-
fill="none"
|
|
75
|
-
// @ts-ignore: svg props
|
|
76
|
-
xmlns="http://www.w3.org/2000/svg">
|
|
72
|
+
<Svg width={size} height={size} viewBox="0 0 16 13" fill="none">
|
|
77
73
|
<Line
|
|
78
74
|
x1="1.64402"
|
|
79
75
|
y1="6.05591"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useEffect
|
|
1
|
+
import {useEffect} from 'react'
|
|
2
2
|
import {View, StyleSheet} from 'react-native'
|
|
3
3
|
import Animated, {
|
|
4
4
|
Easing,
|
|
@@ -13,6 +13,7 @@ import {circleProgressTokens} from '../theme/CircleProgress.recipe'
|
|
|
13
13
|
import {withUnistyles} from 'react-native-unistyles'
|
|
14
14
|
import {resolveColor} from '../style/varia/utils'
|
|
15
15
|
import {PalettesWithNestedKeys} from '../style/varia/types'
|
|
16
|
+
import {SharedValue} from 'react-native-reanimated'
|
|
16
17
|
|
|
17
18
|
const AnimatedCircle = Animated.createAnimatedComponent(Circle)
|
|
18
19
|
const svgSize = 160
|
|
@@ -21,6 +22,8 @@ interface CircleProgressProps {
|
|
|
21
22
|
variant?: keyof typeof circleProgressTokens.variants.variant
|
|
22
23
|
size?: keyof typeof circleProgressTokens.variants.size
|
|
23
24
|
trackStrokeWidth?: number
|
|
25
|
+
progress: SharedValue<number> // ✅ Nuevo
|
|
26
|
+
|
|
24
27
|
progressStrokeWidth?: number
|
|
25
28
|
duration: number
|
|
26
29
|
trackColor?: string
|
|
@@ -37,6 +40,7 @@ const BaseCircleProgress = ({
|
|
|
37
40
|
variant = circleProgressTokens.defaultProps.variant,
|
|
38
41
|
size = circleProgressTokens.defaultProps.size,
|
|
39
42
|
trackStrokeWidth,
|
|
43
|
+
progress,
|
|
40
44
|
progressStrokeWidth,
|
|
41
45
|
duration,
|
|
42
46
|
trackColor,
|
|
@@ -46,17 +50,6 @@ const BaseCircleProgress = ({
|
|
|
46
50
|
children,
|
|
47
51
|
colors,
|
|
48
52
|
}: CircleProgressProps) => {
|
|
49
|
-
const container = useRef<View>(null)
|
|
50
|
-
|
|
51
|
-
// useLayoutEffect(() => {
|
|
52
|
-
// //@ts-ignore
|
|
53
|
-
// container.current?.measure((x, y, width, height) => {
|
|
54
|
-
// const pxPerUnit = width / svgSize
|
|
55
|
-
// const strokeWidthSVG =
|
|
56
|
-
// circleProgressTokens.variants.size[size]?.trackStrokeWidth * pxPerUnit
|
|
57
|
-
// })
|
|
58
|
-
// }, [])
|
|
59
|
-
|
|
60
53
|
const resolvedSize = circleProgressTokens.variants.size[size]
|
|
61
54
|
|
|
62
55
|
const colorValue = circleProgressTokens.variants.variant[variant]
|
|
@@ -80,21 +73,26 @@ const BaseCircleProgress = ({
|
|
|
80
73
|
|
|
81
74
|
const center = svgSize! / 2
|
|
82
75
|
|
|
83
|
-
const
|
|
76
|
+
const internalProgress = useSharedValue(0)
|
|
84
77
|
|
|
85
78
|
useEffect(() => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
if (!progress) {
|
|
80
|
+
internalProgress.value = 0
|
|
81
|
+
internalProgress.value = withTiming(1, {
|
|
82
|
+
duration: duration,
|
|
83
|
+
easing: Easing.linear,
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}, [progress, duration])
|
|
87
|
+
|
|
88
|
+
const progressValue = progress || internalProgress
|
|
91
89
|
|
|
92
90
|
const animatedProps = useAnimatedProps(() => {
|
|
93
91
|
const rStrokeDashOffset = interpolate(
|
|
94
|
-
|
|
92
|
+
progressValue.value,
|
|
95
93
|
[
|
|
96
|
-
progressDirection === 'forward' ?
|
|
97
|
-
progressDirection === 'forward' ?
|
|
94
|
+
progressDirection === 'forward' ? 0 : 1,
|
|
95
|
+
progressDirection === 'forward' ? 1 : 0,
|
|
98
96
|
],
|
|
99
97
|
[circumference, 0],
|
|
100
98
|
Extrapolation.CLAMP,
|
|
@@ -108,7 +106,7 @@ const BaseCircleProgress = ({
|
|
|
108
106
|
})
|
|
109
107
|
|
|
110
108
|
return (
|
|
111
|
-
<View style={styles.container}
|
|
109
|
+
<View style={styles.container}>
|
|
112
110
|
<Svg
|
|
113
111
|
width="100%"
|
|
114
112
|
height="100%"
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useMemo,
|
|
6
|
+
} from 'react'
|
|
7
|
+
import {TouchableWithoutFeedback} from 'react-native'
|
|
8
|
+
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
|
|
9
|
+
import Animated, {
|
|
10
|
+
measure,
|
|
11
|
+
SharedValue,
|
|
12
|
+
useAnimatedReaction,
|
|
13
|
+
useAnimatedRef,
|
|
14
|
+
useAnimatedStyle,
|
|
15
|
+
useSharedValue,
|
|
16
|
+
withSpring,
|
|
17
|
+
withTiming,
|
|
18
|
+
} from 'react-native-reanimated'
|
|
19
|
+
import {runOnUI, scheduleOnRN, scheduleOnUI} from 'react-native-worklets'
|
|
20
|
+
import {
|
|
21
|
+
SlidingDrawerTokens,
|
|
22
|
+
SlidingDrawerStyles,
|
|
23
|
+
} from '../theme/SlidingDrawer.recipe'
|
|
24
|
+
import {PalettesWithNestedKeys} from '../style/varia/types'
|
|
25
|
+
import {
|
|
26
|
+
StyleSheet,
|
|
27
|
+
UnistylesRuntime,
|
|
28
|
+
UnistylesVariants,
|
|
29
|
+
} from 'react-native-unistyles'
|
|
30
|
+
import {HStack} from '../patterns'
|
|
31
|
+
|
|
32
|
+
/* -----------------------------
|
|
33
|
+
* Types
|
|
34
|
+
* ----------------------------*/
|
|
35
|
+
|
|
36
|
+
export type SlidingDrawerRef = {
|
|
37
|
+
expand: () => void | null
|
|
38
|
+
collapse: () => void | null
|
|
39
|
+
snapTo: (point: number) => void | null
|
|
40
|
+
isCollapsed: () => boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type SlidingDrawerVariants = UnistylesVariants<typeof SlidingDrawerStyles>
|
|
44
|
+
|
|
45
|
+
type DrawerContextType = SlidingDrawerVariants & {
|
|
46
|
+
colorPalette: PalettesWithNestedKeys
|
|
47
|
+
overlayOpacity: SharedValue<number>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type DrawerRootProps = SlidingDrawerVariants & {
|
|
51
|
+
colorPalette?: PalettesWithNestedKeys
|
|
52
|
+
children?: React.ReactNode
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* -----------------------------
|
|
56
|
+
* Context
|
|
57
|
+
* ----------------------------*/
|
|
58
|
+
|
|
59
|
+
const DrawerContext = createContext<DrawerContextType | null>(null)
|
|
60
|
+
const useDrawer = () => {
|
|
61
|
+
const ctx = useContext(DrawerContext)
|
|
62
|
+
if (!ctx)
|
|
63
|
+
throw new Error('Drawer subcomponents must be used within Drawer.Root')
|
|
64
|
+
return ctx
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* -----------------------------
|
|
68
|
+
* Root
|
|
69
|
+
* ----------------------------*/
|
|
70
|
+
|
|
71
|
+
const DrawerRoot = ({
|
|
72
|
+
colorPalette = 'accent',
|
|
73
|
+
variant = SlidingDrawerTokens.defaultProps.variant,
|
|
74
|
+
children,
|
|
75
|
+
}: DrawerRootProps) => {
|
|
76
|
+
SlidingDrawerStyles.useVariants({variant})
|
|
77
|
+
|
|
78
|
+
const overlayOpacity = useSharedValue(0)
|
|
79
|
+
const value = useMemo(
|
|
80
|
+
() => ({
|
|
81
|
+
colorPalette,
|
|
82
|
+
variant,
|
|
83
|
+
overlayOpacity,
|
|
84
|
+
}),
|
|
85
|
+
[colorPalette, variant],
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<DrawerContext.Provider value={value}>{children}</DrawerContext.Provider>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* -----------------------------
|
|
94
|
+
* Positioner
|
|
95
|
+
* ----------------------------*/
|
|
96
|
+
const DrawerPositioner = ({
|
|
97
|
+
children,
|
|
98
|
+
direction,
|
|
99
|
+
axis,
|
|
100
|
+
}: {
|
|
101
|
+
children?: React.ReactNode
|
|
102
|
+
direction: 1 | -1
|
|
103
|
+
axis: 'x' | 'y'
|
|
104
|
+
}) => {
|
|
105
|
+
const {variant} = useDrawer()
|
|
106
|
+
SlidingDrawerStyles.useVariants({variant})
|
|
107
|
+
|
|
108
|
+
type WithDirection = {direction?: 1 | -1; axis?: 'x' | 'y'}
|
|
109
|
+
|
|
110
|
+
const enhancedChildren = React.Children.map(children, child => {
|
|
111
|
+
if (React.isValidElement(child)) {
|
|
112
|
+
return React.cloneElement(child as React.ReactElement<WithDirection>, {
|
|
113
|
+
direction,
|
|
114
|
+
axis,
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
return child
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<HStack
|
|
122
|
+
style={[
|
|
123
|
+
styles.positioner,
|
|
124
|
+
SlidingDrawerStyles.positioner,
|
|
125
|
+
{
|
|
126
|
+
justifyContent:
|
|
127
|
+
axis === 'y'
|
|
128
|
+
? 'center'
|
|
129
|
+
: direction === 1
|
|
130
|
+
? 'flex-start'
|
|
131
|
+
: 'flex-end',
|
|
132
|
+
alignItems:
|
|
133
|
+
axis === 'x'
|
|
134
|
+
? 'center'
|
|
135
|
+
: direction === 1
|
|
136
|
+
? 'flex-start'
|
|
137
|
+
: 'flex-end',
|
|
138
|
+
},
|
|
139
|
+
]}>
|
|
140
|
+
{enhancedChildren}
|
|
141
|
+
</HStack>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* -----------------------------
|
|
146
|
+
* Overlay
|
|
147
|
+
* ----------------------------*/
|
|
148
|
+
const AnimatedTouchable = Animated.createAnimatedComponent(
|
|
149
|
+
TouchableWithoutFeedback,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const DrawerOverlay = ({onPress}: {onPress?: () => void}) => {
|
|
153
|
+
const {colorPalette, overlayOpacity, variant} = useDrawer()
|
|
154
|
+
SlidingDrawerStyles.useVariants({variant})
|
|
155
|
+
const visible = useSharedValue(0)
|
|
156
|
+
|
|
157
|
+
useAnimatedReaction(
|
|
158
|
+
() => overlayOpacity.value,
|
|
159
|
+
value => {
|
|
160
|
+
if (value === 0) {
|
|
161
|
+
visible.value = 0
|
|
162
|
+
} else {
|
|
163
|
+
visible.value = 1
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
const displayOverlayStyle = useAnimatedStyle(() => ({
|
|
169
|
+
opacity: withTiming(overlayOpacity.value, {duration: 50}),
|
|
170
|
+
display: visible.value === 0 ? 'none' : 'flex',
|
|
171
|
+
}))
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<AnimatedTouchable onPress={onPress}>
|
|
175
|
+
<Animated.View
|
|
176
|
+
style={[
|
|
177
|
+
StyleSheet.absoluteFillObject,
|
|
178
|
+
displayOverlayStyle,
|
|
179
|
+
SlidingDrawerStyles.overlay(colorPalette),
|
|
180
|
+
]}
|
|
181
|
+
/>
|
|
182
|
+
</AnimatedTouchable>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* -----------------------------
|
|
187
|
+
* Slider (gestión independiente)
|
|
188
|
+
* ----------------------------*/
|
|
189
|
+
|
|
190
|
+
type InternalDrawerSliderProps = {
|
|
191
|
+
axis: 'x' | 'y'
|
|
192
|
+
direction: 1 | -1
|
|
193
|
+
lockAtEdges?: boolean
|
|
194
|
+
snapPoints?: number[] | ('hidden' | 'content')[]
|
|
195
|
+
animation?: keyof typeof SlidingDrawerTokens.variants.animation
|
|
196
|
+
onExpand?: () => void
|
|
197
|
+
onCollapse?: () => void
|
|
198
|
+
onSnap?: (point: number) => void
|
|
199
|
+
allowGestures?: boolean
|
|
200
|
+
children?: React.ReactNode
|
|
201
|
+
ref?: React.RefObject<SlidingDrawerRef | null>
|
|
202
|
+
externalTranslate?: SharedValue<number>
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Tipo público — el que exportas (sin direction)
|
|
206
|
+
export type DrawerSliderProps = Omit<
|
|
207
|
+
InternalDrawerSliderProps,
|
|
208
|
+
'direction' | 'axis'
|
|
209
|
+
>
|
|
210
|
+
|
|
211
|
+
export const DrawerSlider = (props: DrawerSliderProps) => {
|
|
212
|
+
return <DrawerSliderInternal {...(props as InternalDrawerSliderProps)} />
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const DrawerSliderInternal = ({
|
|
216
|
+
axis,
|
|
217
|
+
direction,
|
|
218
|
+
lockAtEdges = false,
|
|
219
|
+
snapPoints = ['hidden', 'content'],
|
|
220
|
+
animation = SlidingDrawerTokens.defaultProps.animation,
|
|
221
|
+
onExpand,
|
|
222
|
+
onCollapse,
|
|
223
|
+
onSnap,
|
|
224
|
+
allowGestures = true,
|
|
225
|
+
children,
|
|
226
|
+
externalTranslate,
|
|
227
|
+
ref,
|
|
228
|
+
}: InternalDrawerSliderProps) => {
|
|
229
|
+
const {variant, colorPalette, overlayOpacity} = useDrawer()
|
|
230
|
+
|
|
231
|
+
SlidingDrawerStyles.useVariants({variant})
|
|
232
|
+
|
|
233
|
+
const screenHeight =
|
|
234
|
+
UnistylesRuntime.screen.height - UnistylesRuntime.insets.top
|
|
235
|
+
const screenWidth = UnistylesRuntime.screen.width
|
|
236
|
+
|
|
237
|
+
const animations = {withSpring, withTiming}
|
|
238
|
+
const animationVariant = SlidingDrawerTokens.variants.animation[animation]
|
|
239
|
+
const VELOCITY_THRESHOLD = 2000
|
|
240
|
+
|
|
241
|
+
// --- shared values base ---
|
|
242
|
+
const viewRef = useAnimatedRef<Animated.View>()
|
|
243
|
+
const translate = useSharedValue(screenHeight)
|
|
244
|
+
const context = useSharedValue({position: screenHeight, snapPoint: 0})
|
|
245
|
+
const contentHeight = useSharedValue(0)
|
|
246
|
+
const resolvedSnapPoints = useSharedValue<number[]>([])
|
|
247
|
+
|
|
248
|
+
if (externalTranslate) {
|
|
249
|
+
useAnimatedReaction(
|
|
250
|
+
() => translate.value,
|
|
251
|
+
value => {
|
|
252
|
+
externalTranslate.value = value
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// --- medir el contenido dinámicamente ---
|
|
258
|
+
// const onLayout = () => {
|
|
259
|
+
// scheduleOnUI(() => {
|
|
260
|
+
// 'worklet'
|
|
261
|
+
// const measured = measure(viewRef)
|
|
262
|
+
// if (measured) {
|
|
263
|
+
// const {height} = measured
|
|
264
|
+
// contentHeight.value = height
|
|
265
|
+
|
|
266
|
+
// // resuelve snapPoints declarativos
|
|
267
|
+
// const resolved = snapPoints.map(p => {
|
|
268
|
+
// if (p === 'hidden') return screenHeight * direction
|
|
269
|
+
// if (p === 'content') return (screenHeight - height) * direction
|
|
270
|
+
// // if (typeof p === 'string' && p.endsWith('%')) {
|
|
271
|
+
// // const percentage = parseFloat(p) / 100
|
|
272
|
+
// // return screenHeight * (1 - percentage) * direction
|
|
273
|
+
// // }
|
|
274
|
+
// return p * direction
|
|
275
|
+
// })
|
|
276
|
+
|
|
277
|
+
// resolvedSnapPoints.value = resolved
|
|
278
|
+
|
|
279
|
+
// // inicializa la posición (oculto)
|
|
280
|
+
// translate.value = resolved[0]
|
|
281
|
+
// context.value = {position: resolved[0], snapPoint: 0}
|
|
282
|
+
// }
|
|
283
|
+
// })
|
|
284
|
+
// }
|
|
285
|
+
|
|
286
|
+
const onLayout = () => {
|
|
287
|
+
// captura valores aquí (en JS)
|
|
288
|
+
const _screenHeight = screenHeight
|
|
289
|
+
const _screenWidth = screenWidth
|
|
290
|
+
const _axis = axis
|
|
291
|
+
const _direction = direction
|
|
292
|
+
const _snapPoints = snapPoints
|
|
293
|
+
|
|
294
|
+
scheduleOnUI(() => {
|
|
295
|
+
'worklet'
|
|
296
|
+
const measured = measure(viewRef)
|
|
297
|
+
if (measured) {
|
|
298
|
+
const {height, width} = measured
|
|
299
|
+
const size = _axis === 'y' ? height : width
|
|
300
|
+
const screenSize = _axis === 'y' ? _screenHeight : _screenWidth
|
|
301
|
+
|
|
302
|
+
const resolved = _snapPoints.map(p => {
|
|
303
|
+
if (p === 'hidden') return screenSize * _direction
|
|
304
|
+
if (p === 'content') return (screenSize - size) * _direction
|
|
305
|
+
return (p as number) * _direction
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
resolvedSnapPoints.value = resolved
|
|
309
|
+
translate.value = resolved[0]
|
|
310
|
+
context.value = {position: resolved[0], snapPoint: 0}
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const getPoints = () => {
|
|
316
|
+
'worklet'
|
|
317
|
+
return resolvedSnapPoints.value.length > 0
|
|
318
|
+
? resolvedSnapPoints.value
|
|
319
|
+
: snapPoints.map(p => (p as any) * direction)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// --- lógica de snapping ---
|
|
323
|
+
const updateCurrentSnapPoint = (snapPoint: number) => {
|
|
324
|
+
'worklet'
|
|
325
|
+
context.value = {position: context.value.position, snapPoint}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const snapTo = (destination: number) => {
|
|
329
|
+
'worklet'
|
|
330
|
+
const points = getPoints()
|
|
331
|
+
const point = points[destination]
|
|
332
|
+
if (animationVariant?.type && point != null) {
|
|
333
|
+
translate.value = animations[animationVariant.type](
|
|
334
|
+
point,
|
|
335
|
+
animationVariant.props,
|
|
336
|
+
)
|
|
337
|
+
updateCurrentSnapPoint(destination)
|
|
338
|
+
onSnap && scheduleOnRN(onSnap, destination)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const isCollapsed = () => {
|
|
343
|
+
'worklet'
|
|
344
|
+
return context.value.snapPoint === 0
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const showOverlay = () => {
|
|
348
|
+
'worklet'
|
|
349
|
+
overlayOpacity.value = withTiming(1, {duration: 200})
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const hideOverlay = () => {
|
|
353
|
+
'worklet'
|
|
354
|
+
overlayOpacity.value = withTiming(0, {duration: 200})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const expand = () => {
|
|
358
|
+
const points = getPoints()
|
|
359
|
+
snapTo(points.length - 1)
|
|
360
|
+
showOverlay()
|
|
361
|
+
onExpand && scheduleOnRN(onExpand)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const collapse = () => {
|
|
365
|
+
snapTo(0)
|
|
366
|
+
hideOverlay()
|
|
367
|
+
onCollapse && scheduleOnRN(onCollapse)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
useImperativeHandle(ref, () => ({
|
|
371
|
+
expand,
|
|
372
|
+
collapse,
|
|
373
|
+
snapTo,
|
|
374
|
+
isCollapsed,
|
|
375
|
+
}))
|
|
376
|
+
|
|
377
|
+
// --- sincronizar overlay con movimiento ---
|
|
378
|
+
useAnimatedReaction(
|
|
379
|
+
() => translate.value,
|
|
380
|
+
value => {
|
|
381
|
+
const points = getPoints()
|
|
382
|
+
if (points.length < 2) return
|
|
383
|
+
|
|
384
|
+
const collapsed = points[0]
|
|
385
|
+
const next = points[1] ?? collapsed
|
|
386
|
+
const threshold = 20
|
|
387
|
+
const opensUpward = next < collapsed
|
|
388
|
+
const delta = value - collapsed
|
|
389
|
+
const isExpanded = opensUpward ? delta < -threshold : delta > threshold
|
|
390
|
+
|
|
391
|
+
overlayOpacity.value = withTiming(isExpanded ? 1 : 0, {duration: 150})
|
|
392
|
+
},
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
// --- Gestures ---
|
|
396
|
+
const slideGesture = Gesture.Pan()
|
|
397
|
+
.enabled(allowGestures)
|
|
398
|
+
.onBegin(() => {
|
|
399
|
+
context.value.position = translate.value
|
|
400
|
+
})
|
|
401
|
+
.onUpdate(event => {
|
|
402
|
+
const delta = axis === 'y' ? event.translationY : event.translationX
|
|
403
|
+
const proposed = delta + (context.value.position ?? 0)
|
|
404
|
+
const points = getPoints()
|
|
405
|
+
const minPoint = Math.min(...points)
|
|
406
|
+
const maxPoint = Math.max(...points)
|
|
407
|
+
let clamped = proposed
|
|
408
|
+
|
|
409
|
+
if (proposed < minPoint) {
|
|
410
|
+
if (lockAtEdges) {
|
|
411
|
+
// Bloquea overscroll superior (cuando ya estás en el extremo)
|
|
412
|
+
clamped = minPoint
|
|
413
|
+
} else {
|
|
414
|
+
// Aplica resistencia
|
|
415
|
+
const overdrag = minPoint - proposed
|
|
416
|
+
clamped = minPoint - overdrag / (1 + overdrag / 60)
|
|
417
|
+
}
|
|
418
|
+
} else if (proposed > maxPoint) {
|
|
419
|
+
if (lockAtEdges) {
|
|
420
|
+
// Bloquea overscroll inferior
|
|
421
|
+
clamped = maxPoint
|
|
422
|
+
} else {
|
|
423
|
+
const overdrag = proposed - maxPoint
|
|
424
|
+
clamped = maxPoint + overdrag / (1 + overdrag / 60)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
translate.value = clamped
|
|
429
|
+
})
|
|
430
|
+
.onEnd(({velocityX, velocityY, translationX, translationY}) => {
|
|
431
|
+
const velocity = axis === 'y' ? velocityY : velocityX
|
|
432
|
+
const translation = axis === 'y' ? translationY : translationX
|
|
433
|
+
const points = getPoints()
|
|
434
|
+
|
|
435
|
+
const minPoint = Math.min(...points)
|
|
436
|
+
const maxPoint = Math.max(...points)
|
|
437
|
+
|
|
438
|
+
if (translate.value < minPoint) {
|
|
439
|
+
translate.value = withSpring(minPoint, {velocity})
|
|
440
|
+
return
|
|
441
|
+
} else if (translate.value > maxPoint) {
|
|
442
|
+
translate.value = withSpring(maxPoint, {velocity})
|
|
443
|
+
return
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const forwardsThreshold =
|
|
447
|
+
(points[context.value.snapPoint] +
|
|
448
|
+
points[context.value.snapPoint + 1]) /
|
|
449
|
+
2
|
|
450
|
+
const backwardsThreshold =
|
|
451
|
+
(points[context.value.snapPoint] +
|
|
452
|
+
points[context.value.snapPoint - 1]) /
|
|
453
|
+
2
|
|
454
|
+
|
|
455
|
+
if (translation * direction < 0) {
|
|
456
|
+
if (
|
|
457
|
+
((direction === 1 && translate.value < forwardsThreshold) ||
|
|
458
|
+
(direction === -1 && translate.value > forwardsThreshold) ||
|
|
459
|
+
velocity * direction < -VELOCITY_THRESHOLD) &&
|
|
460
|
+
context.value.snapPoint < points.length - 1
|
|
461
|
+
) {
|
|
462
|
+
snapTo(context.value.snapPoint + 1)
|
|
463
|
+
} else {
|
|
464
|
+
snapTo(context.value.snapPoint)
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
if (
|
|
468
|
+
((direction === 1 && translate.value > backwardsThreshold) ||
|
|
469
|
+
(direction === -1 && translate.value < backwardsThreshold) ||
|
|
470
|
+
velocity * direction > VELOCITY_THRESHOLD) &&
|
|
471
|
+
context.value.snapPoint > 0
|
|
472
|
+
) {
|
|
473
|
+
snapTo(context.value.snapPoint - 1)
|
|
474
|
+
} else {
|
|
475
|
+
snapTo(context.value.snapPoint)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
// --- estilos animados ---
|
|
481
|
+
const blockAnimatedStyle = useAnimatedStyle(() => {
|
|
482
|
+
return {
|
|
483
|
+
transform: [
|
|
484
|
+
axis === 'y'
|
|
485
|
+
? {translateY: translate.value}
|
|
486
|
+
: {translateX: translate.value},
|
|
487
|
+
],
|
|
488
|
+
}
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<GestureDetector gesture={slideGesture}>
|
|
493
|
+
<Animated.View
|
|
494
|
+
ref={viewRef}
|
|
495
|
+
onLayout={onLayout}
|
|
496
|
+
style={[
|
|
497
|
+
styles.slider,
|
|
498
|
+
blockAnimatedStyle,
|
|
499
|
+
SlidingDrawerStyles.slider(colorPalette),
|
|
500
|
+
]}>
|
|
501
|
+
{children}
|
|
502
|
+
</Animated.View>
|
|
503
|
+
</GestureDetector>
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* -----------------------------
|
|
508
|
+
* Export grouped
|
|
509
|
+
* ----------------------------*/
|
|
510
|
+
|
|
511
|
+
export const Drawer = {
|
|
512
|
+
Root: DrawerRoot,
|
|
513
|
+
Positioner: DrawerPositioner,
|
|
514
|
+
Overlay: DrawerOverlay,
|
|
515
|
+
Slider: DrawerSlider,
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* -----------------------------
|
|
519
|
+
* Styles
|
|
520
|
+
* ----------------------------*/
|
|
521
|
+
|
|
522
|
+
const styles = StyleSheet.create((theme, rt) => ({
|
|
523
|
+
positioner: {
|
|
524
|
+
flex: 1,
|
|
525
|
+
alignSelf: 'stretch',
|
|
526
|
+
position: 'absolute',
|
|
527
|
+
top: 0,
|
|
528
|
+
left: 0,
|
|
529
|
+
right: 0,
|
|
530
|
+
bottom: 0,
|
|
531
|
+
},
|
|
532
|
+
slider: {
|
|
533
|
+
flex: 1,
|
|
534
|
+
alignSelf: 'stretch',
|
|
535
|
+
alignItems: 'center',
|
|
536
|
+
justifyContent: 'flex-start',
|
|
537
|
+
zIndex: 100,
|
|
538
|
+
},
|
|
539
|
+
}))
|
package/lib/components/Field.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, {createContext, useContext} from 'react'
|
|
2
|
+
import {UnistylesVariants} from 'react-native-unistyles'
|
|
2
3
|
import Text from './Text'
|
|
3
4
|
import {VStack} from '../patterns'
|
|
4
5
|
import {DefaultFieldVariants, FieldStyles} from '../theme/Field.recipe'
|
|
5
|
-
import {UnistylesVariants} from 'react-native-unistyles'
|
|
6
6
|
import {PalettesWithNestedKeys} from '../style/varia/types'
|
|
7
7
|
|
|
8
8
|
type FieldVariants = UnistylesVariants<typeof FieldStyles>
|
|
@@ -90,10 +90,7 @@ const FieldLabel = ({children, ...props}: FieldLabelProps) => {
|
|
|
90
90
|
FieldStyles.useVariants({variant, size})
|
|
91
91
|
|
|
92
92
|
return (
|
|
93
|
-
<Text
|
|
94
|
-
size={size}
|
|
95
|
-
// @ts-ignore
|
|
96
|
-
color={FieldStyles.label(colorPalette).color}>
|
|
93
|
+
<Text size={size} color={FieldStyles.label(colorPalette).color}>
|
|
97
94
|
{children}
|
|
98
95
|
</Text>
|
|
99
96
|
)
|
|
@@ -121,7 +118,6 @@ const FieldError = ({...props}: FieldErrorProps) => {
|
|
|
121
118
|
FieldStyles.useVariants({size, variant})
|
|
122
119
|
|
|
123
120
|
return (
|
|
124
|
-
// @ts-ignore
|
|
125
121
|
<Text size={size} color={FieldStyles.error(colorPalette).color}>
|
|
126
122
|
{error}
|
|
127
123
|
</Text>
|
package/lib/components/Input.tsx
CHANGED
|
@@ -66,6 +66,11 @@ const Input = ({
|
|
|
66
66
|
invalid,
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
+
const placeholderStyle = InputStyles.placeholder(colorPalette) as {
|
|
70
|
+
color: string
|
|
71
|
+
}
|
|
72
|
+
const placeholderColor = placeholderStyle.color
|
|
73
|
+
|
|
69
74
|
return (
|
|
70
75
|
<HStack
|
|
71
76
|
style={{
|
|
@@ -82,8 +87,7 @@ const Input = ({
|
|
|
82
87
|
]}
|
|
83
88
|
value={value}
|
|
84
89
|
placeholder={placeholder}
|
|
85
|
-
|
|
86
|
-
placeholderTextColor={InputStyles.placeholder(colorPalette).color}
|
|
90
|
+
placeholderTextColor={placeholderColor}
|
|
87
91
|
inputMode={inputMode}
|
|
88
92
|
keyboardType={keyboardType}
|
|
89
93
|
secureTextEntry={password}
|