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
@@ -1,327 +0,0 @@
1
- import React from 'react'
2
- import {type LayoutChangeEvent, View} from 'react-native'
3
- import {StyleSheet, UnistylesVariants} from 'react-native-unistyles'
4
- import {Gesture, GestureDetector} from 'react-native-gesture-handler'
5
- import Animated, {
6
- useAnimatedStyle,
7
- useSharedValue,
8
- } from 'react-native-reanimated'
9
- import {runOnJS} from 'react-native-worklets'
10
- import {SliderStyles, SliderDefaultVariants} from '../theme/OldSlider.recipe'
11
- import {PalettesWithNestedKeys} from '../style/varia/types'
12
-
13
- type SliderVariants = UnistylesVariants<typeof SliderStyles>
14
-
15
- type SliderProps = SliderVariants & {
16
- colorPalette?: PalettesWithNestedKeys
17
- thickness?: number
18
- minimumTrackThickness?: number
19
- minimumTrackColor?: string
20
- displayStepsOnMinimumTrack?: boolean
21
- thumbSize?: {
22
- width: number
23
- height: number
24
- }
25
- thumbChildren?: React.ReactNode
26
- axis: 'x' | 'y'
27
- steps?: number
28
- value?: number
29
- onValueChange?: (value: number) => void
30
- onSlidingComplete?: (value: number) => void
31
- }
32
-
33
- const OldSlider = ({
34
- colorPalette = 'accent',
35
- variant = SliderDefaultVariants.variant,
36
- size = SliderDefaultVariants.size,
37
- displayStepsOnMinimumTrack = false,
38
- thumbChildren: ThumbChildren,
39
- axis = 'x',
40
- value = 0,
41
- steps,
42
- onValueChange,
43
- onSlidingComplete,
44
- }: SliderProps) => {
45
- let styles
46
- if (axis === 'x') {
47
- styles = stylesX
48
- } else {
49
- styles = stylesY
50
- }
51
- SliderStyles.useVariants({
52
- size,
53
- variant,
54
- })
55
- const thumbStyle = SliderStyles.thumb(colorPalette)
56
- const maximumTrackStyle = SliderStyles.maximumTrack(colorPalette)
57
- const minimumTrackStyle = SliderStyles.minimumTrack(colorPalette, false)
58
-
59
- const halfSize = (thumbStyle.width ?? 0) / 2
60
- const thumbSize = {
61
- width: thumbStyle.width,
62
- height: thumbStyle.height,
63
- }
64
- const maximumTrackWidth = maximumTrackStyle.height ?? 30
65
- const minimumTrackWidth = minimumTrackStyle.height ?? 30
66
- const context = useSharedValue({x: 0})
67
- const translate = useSharedValue(value)
68
- const trackLength = useSharedValue(0)
69
- const isInsideChild = useSharedValue(false)
70
-
71
- const handleTrackLayout = (event: LayoutChangeEvent) => {
72
- const {width, height} = event.nativeEvent.layout
73
- trackLength.value = axis === 'x' ? width : height
74
- }
75
-
76
- const slideGesture = Gesture.Pan()
77
- .onTouchesDown(() => {
78
- isInsideChild.value = true
79
- })
80
- .onBegin(() => {
81
- context.value.x = translate.value
82
- })
83
- .onUpdate(e => {
84
- const stepLength = steps ? trackLength.value / steps : 1
85
- const delta = axis === 'y' ? -e.translationY : e.translationX
86
-
87
- const rawValue = Math.max(
88
- 0,
89
- Math.min(context.value.x + delta, trackLength.value),
90
- )
91
-
92
- const snappedValue = steps
93
- ? Math.round(rawValue / stepLength) * stepLength
94
- : rawValue
95
-
96
- translate.value = snappedValue
97
-
98
- if (steps) {
99
- const stepIndex = Math.round(snappedValue / stepLength)
100
- onValueChange && runOnJS(onValueChange)(stepIndex)
101
- } else {
102
- onValueChange &&
103
- runOnJS(onValueChange)(
104
- Math.round((snappedValue / trackLength.value) * 100) / 100,
105
- )
106
- }
107
- })
108
- .onEnd(() => {
109
- const stepLength = steps ? trackLength.value / steps : 1
110
-
111
- const snappedValue = steps
112
- ? Math.round(translate.value / stepLength)
113
- : translate.value / trackLength.value
114
-
115
- onSlidingComplete && runOnJS(onSlidingComplete)(snappedValue)
116
- })
117
- .onFinalize(() => {
118
- isInsideChild.value = false
119
- })
120
- const tapGesture = Gesture.Tap()
121
- .onBegin(e => {
122
- if (isInsideChild.value) {
123
- return
124
- }
125
- const tapPosition = axis === 'x' ? e.x : e.y
126
- const stepLength = steps ? trackLength.value / steps : 1
127
-
128
- const rawValue =
129
- axis === 'y'
130
- ? Math.max(
131
- 0,
132
- Math.min(trackLength.value - tapPosition, trackLength.value),
133
- )
134
- : Math.max(0, Math.min(tapPosition, trackLength.value))
135
-
136
- const snappedValue = steps
137
- ? Math.round(rawValue / stepLength) * stepLength
138
- : rawValue
139
-
140
- translate.value = snappedValue
141
-
142
- if (steps) {
143
- const stepIndex = Math.round(snappedValue / stepLength)
144
- onValueChange && runOnJS(onValueChange)(stepIndex)
145
- onSlidingComplete && runOnJS(onSlidingComplete)(stepIndex)
146
- } else {
147
- const normalizedValue =
148
- Math.round((snappedValue / trackLength.value) * 100) / 100
149
- onValueChange && runOnJS(onValueChange)(normalizedValue)
150
- onSlidingComplete && runOnJS(onSlidingComplete)(normalizedValue)
151
- }
152
- })
153
- .simultaneousWithExternalGesture(slideGesture)
154
-
155
- const animatedTrack = useAnimatedStyle(() => ({
156
- [axis === 'y' ? 'height' : 'width']: translate.value + halfSize,
157
- }))
158
-
159
- const opacityTrack = steps !== undefined && displayStepsOnMinimumTrack
160
-
161
- return (
162
- <View
163
- style={[
164
- styles.container(maximumTrackWidth, halfSize),
165
- SliderStyles.container(colorPalette),
166
- ]}>
167
- {axis === 'x' && <View style={{width: halfSize}} />}
168
- <GestureDetector gesture={tapGesture}>
169
- <View
170
- style={[
171
- styles.maximumTrack(maximumTrackWidth),
172
- SliderStyles.maximumTrack(colorPalette),
173
- ]}
174
- onLayout={handleTrackLayout}>
175
- {steps && (
176
- <View style={styles.steps}>
177
- {Array.from({length: steps + 1}, (_, index) => index).map(
178
- (_, i) => (
179
- <View
180
- key={i}
181
- style={[
182
- styles.step(i, steps),
183
- SliderStyles.step(colorPalette),
184
- ]}
185
- />
186
- ),
187
- )}
188
- </View>
189
- )}
190
- <Animated.View
191
- style={[
192
- styles.minimumTrack(halfSize, minimumTrackWidth),
193
- SliderStyles.minimumTrack(colorPalette, opacityTrack),
194
- animatedTrack,
195
- ]}>
196
- <GestureDetector gesture={slideGesture}>
197
- <View
198
- style={[
199
- SliderStyles.thumb(colorPalette),
200
- styles.thumb(halfSize, thumbSize),
201
- ]}>
202
- {ThumbChildren || null}
203
- </View>
204
- </GestureDetector>
205
- </Animated.View>
206
- </View>
207
- </GestureDetector>
208
- {axis === 'y' && <View style={{height: halfSize}} />}
209
- </View>
210
- )
211
- }
212
-
213
- const stylesX = StyleSheet.create(theme => ({
214
- container: (thickness, halfSize) => ({
215
- flex: thickness ? 1 : 1,
216
- maxWidth: 'auto',
217
- borderRadius: 22,
218
- width: '100%',
219
- height: '100%',
220
- paddingTop: 0,
221
- paddingRight: halfSize,
222
- flexDirection: 'row',
223
- }),
224
- maximumTrack: thickness => ({
225
- flex: thickness ? 1 : 1,
226
- justifyContent: 'center',
227
- position: 'relative',
228
- borderRadius: 22,
229
- }),
230
- minimumTrack: (halfSize, minimumTrackThickness) => ({
231
- height: minimumTrackThickness,
232
- width: '100%',
233
- left: halfSize * -1,
234
- bottom: 'auto',
235
- paddingBottom: 0,
236
- paddingLeft: halfSize,
237
- borderBottomLeftRadius: 20,
238
- borderBottomRightRadius: 0,
239
- borderTopLeftRadius: 20,
240
- justifyContent: 'center',
241
- }),
242
- thumb: (halfSize, thumbSize) => ({
243
- width: halfSize ? thumbSize.width : thumbSize.width,
244
- height: thumbSize.height,
245
- borderRadius: 25,
246
- position: 'absolute',
247
- right: 0,
248
- zIndex: 2,
249
- transform: [{translateX: '50%'}],
250
- }),
251
- steps: {
252
- width: '100%',
253
- height: '100%',
254
- flexDirection: 'row',
255
- justifyContent: 'space-between',
256
- alignItems: 'center',
257
- zIndex: 0,
258
- position: 'absolute',
259
- },
260
- step: (index, length) => ({
261
- width: 1,
262
- height: '100%',
263
- backgroundColor:
264
- index === 0 || index === length ? 'transparent' : theme.colors.fg.default,
265
- }),
266
- }))
267
- const stylesY = StyleSheet.create(theme => ({
268
- container: (maximumTrackWidth, halfSize) => ({
269
- flex: 1,
270
- maxWidth: maximumTrackWidth,
271
- maxHeight: 'auto',
272
- borderRadius: 22,
273
- width: '100%',
274
- height: '100%',
275
- paddingTop: halfSize,
276
- paddingRight: 0,
277
- flexDirection: 'column',
278
- borderWidth: 1,
279
- borderColor: 'yellow',
280
- // minWidth: 30,
281
- }),
282
- maximumTrack: maximumTrackWidth => ({
283
- flex: 1,
284
- justifyContent: 'center',
285
- alignItems: 'center',
286
- position: 'relative',
287
- maxWidth: maximumTrackWidth,
288
- }),
289
- minimumTrack: (halfSize, minimumTrackWidth) => ({
290
- position: 'absolute',
291
- height: '100%',
292
- width: minimumTrackWidth,
293
- left: 'auto',
294
- bottom: halfSize * -1,
295
- paddingBottom: halfSize,
296
- paddingLeft: 0,
297
- borderBottomLeftRadius: 20,
298
- borderBottomRightRadius: 20,
299
- borderTopLeftRadius: 0,
300
- alignItems: 'center',
301
- }),
302
- thumb: (halfSize, thumbSize) => ({
303
- borderRadius: 25,
304
- position: 'absolute',
305
- top: 0,
306
- zIndex: 2,
307
- transform: [{translateY: halfSize * -1}],
308
- width: thumbSize.height,
309
- height: thumbSize.width,
310
- }),
311
- steps: {
312
- width: '100%',
313
- height: '100%',
314
- flexDirection: 'column',
315
- justifyContent: 'space-between',
316
- alignItems: 'center',
317
- zIndex: 0,
318
- },
319
- step: (index, length) => ({
320
- width: '100%',
321
- height: 1,
322
- backgroundColor:
323
- index === 0 || index === length ? 'transparent' : theme.colors.fg.default,
324
- }),
325
- }))
326
-
327
- export default OldSlider
@@ -1,301 +0,0 @@
1
- import React, {useImperativeHandle} from 'react'
2
- import {StyleSheet, UnistylesVariants} from 'react-native-unistyles'
3
- import {Gesture, GestureDetector} from 'react-native-gesture-handler'
4
- import Animated, {
5
- useAnimatedStyle,
6
- useSharedValue,
7
- withSpring,
8
- withTiming,
9
- type SharedValue,
10
- } from 'react-native-reanimated'
11
- import {scheduleOnRN} from 'react-native-worklets'
12
- import {
13
- SlidingDrawerTokens,
14
- SlidingDrawerStyles,
15
- } from '../theme/SlidingDrawer.recipe'
16
- import {PalettesWithNestedKeys} from '../style/varia/types'
17
- import {TouchableWithoutFeedback, ViewStyle} from 'react-native'
18
-
19
- export type SlidingDrawerRef = {
20
- snapTo: (point: number) => void | null
21
- expand: () => void | null
22
- collapse: () => void | null
23
- }
24
-
25
- type SlidingDrawerVariants = UnistylesVariants<typeof SlidingDrawerStyles>
26
-
27
- type SlidingDrawerProps = SlidingDrawerVariants & {
28
- colorPalette?: PalettesWithNestedKeys
29
- animation?: keyof typeof SlidingDrawerTokens.variants.animation
30
- width?: string
31
- flex?: number
32
- direction?: 1 | -1
33
- onCollapse?: () => void
34
- onExpand?: () => void
35
- onSnap?: (point: number) => void
36
- snapPoints: number[]
37
- axis: 'y' | 'x'
38
- allowGestures?: boolean
39
- overlay?: boolean
40
- closeOnOverlayPress?: boolean
41
- children?: React.ReactNode
42
- ref?: React.RefObject<SlidingDrawerRef | null>
43
- }
44
-
45
- const AnimatedTouchableOpacity = Animated.createAnimatedComponent(
46
- TouchableWithoutFeedback,
47
- )
48
-
49
- const SlidingDrawer = ({
50
- colorPalette = 'accent',
51
- variant = SlidingDrawerTokens.defaultProps.variant,
52
- animation = SlidingDrawerTokens.defaultProps.animation,
53
- width = '100%',
54
- flex = 0,
55
- direction = 1,
56
- onCollapse,
57
- onExpand,
58
- onSnap,
59
- snapPoints,
60
- axis,
61
- allowGestures = true,
62
- overlay = false,
63
- closeOnOverlayPress = true,
64
- children,
65
- ref,
66
- }: SlidingDrawerProps) => {
67
- SlidingDrawerStyles.useVariants({
68
- variant,
69
- })
70
-
71
- const points = snapPoints.map(point => point * direction)
72
-
73
- const translate = useSharedValue(points[0])
74
-
75
- const context: SharedValue<{
76
- position: number
77
- snapPoint: number
78
- }> = useSharedValue({
79
- position: points[0],
80
- snapPoint: 0,
81
- })
82
-
83
- const changeDisplay = useSharedValue('none')
84
- const opacity = useSharedValue(0)
85
-
86
- const VELOCITY_THRESHOLD = 2000
87
-
88
- const updateCurrentSnapPoint = (snapPoint: number) => {
89
- 'worklet'
90
- context.value = {position: context.value.position, snapPoint}
91
- }
92
-
93
- const animations = {
94
- withSpring,
95
- withTiming,
96
- }
97
-
98
- const animationVariant = SlidingDrawerTokens.variants.animation[animation]
99
-
100
- const snapTo = (destination: number) => {
101
- 'worklet'
102
-
103
- if (animationVariant && animationVariant.type) {
104
- translate.value = animations[animationVariant.type](
105
- points[destination],
106
- animationVariant.props,
107
- )
108
- updateCurrentSnapPoint(destination)
109
- onSnap && scheduleOnRN(onSnap, destination)
110
- }
111
- }
112
-
113
- const expand = () => {
114
- snapTo(points.length - 1)
115
- if (overlay) {
116
- showOverlay()
117
- }
118
- onExpand && scheduleOnRN(onExpand)
119
- }
120
-
121
- const isCollapsed = () => {
122
- 'worklet'
123
- return context.value.snapPoint === 0
124
- }
125
-
126
- const collapse = () => {
127
- snapTo(0)
128
- if (overlay) {
129
- hideOverlay()
130
- }
131
- onCollapse && scheduleOnRN(onCollapse)
132
- }
133
-
134
- const hideOverlay = () => {
135
- 'worklet'
136
- changeDisplay.value = 'none'
137
- opacity.value = withTiming(0, {duration: 200})
138
- }
139
- const showOverlay = () => {
140
- 'worklet'
141
- changeDisplay.value = 'flex'
142
- opacity.value = withTiming(1, {duration: 200})
143
- }
144
-
145
- useImperativeHandle(ref, () => ({
146
- expand,
147
- collapse,
148
- snapTo,
149
- isCollapsed,
150
- }))
151
-
152
- const slideGesture = Gesture.Pan()
153
- .enabled(allowGestures)
154
- .onBegin(() => {
155
- context.value.position = translate.value
156
- })
157
- .onUpdate(event => {
158
- const delta = axis === 'y' ? event.translationY : event.translationX
159
-
160
- const proposed = delta + (context.value.position ?? 0)
161
-
162
- const minPoint = Math.min(...points)
163
- const maxPoint = Math.max(...points)
164
-
165
- let clamped = proposed
166
-
167
- if (proposed < minPoint) {
168
- const overdrag = minPoint - proposed
169
- clamped = minPoint - overdrag / (1 + overdrag / 60)
170
- } else if (proposed > maxPoint) {
171
- const overdrag = proposed - maxPoint
172
- clamped = maxPoint + overdrag / (1 + overdrag / 60)
173
- }
174
-
175
- translate.value = clamped
176
- })
177
- .onEnd(({velocityX, velocityY, translationX, translationY}) => {
178
- const velocity = axis === 'y' ? velocityY : velocityX
179
- const translation = axis === 'y' ? translationY : translationX
180
-
181
- const minPoint = Math.min(...points)
182
- const maxPoint = Math.max(...points)
183
-
184
- if (translate.value < minPoint) {
185
- translate.value = withSpring(minPoint, {velocity})
186
- return
187
- } else if (translate.value > maxPoint) {
188
- translate.value = withSpring(maxPoint, {velocity})
189
- return
190
- }
191
-
192
- const forwardsThreshold =
193
- (points[context.value.snapPoint] +
194
- points[context.value.snapPoint + 1]) /
195
- 2
196
- const backwardsThreshold =
197
- (points[context.value.snapPoint] +
198
- points[context.value.snapPoint - 1]) /
199
- 2
200
-
201
- if (translation * direction < 0) {
202
- if (
203
- ((direction === 1 && translate.value < forwardsThreshold) ||
204
- (direction === -1 && translate.value > forwardsThreshold) ||
205
- velocity * direction < -VELOCITY_THRESHOLD) &&
206
- context.value.snapPoint < points.length - 1
207
- ) {
208
- snapTo(context.value.snapPoint + 1)
209
- } else {
210
- snapTo(context.value.snapPoint)
211
- }
212
- } else {
213
- if (
214
- ((direction === 1 && translate.value > backwardsThreshold) ||
215
- (direction === -1 && translate.value < backwardsThreshold) ||
216
- velocity * direction > VELOCITY_THRESHOLD) &&
217
- context.value.snapPoint > 0
218
- ) {
219
- snapTo(context.value.snapPoint - 1)
220
- } else {
221
- snapTo(context.value.snapPoint)
222
- }
223
- }
224
- })
225
- .onFinalize(() => {
226
- if (overlay) {
227
- if (isCollapsed()) {
228
- hideOverlay()
229
- } else {
230
- showOverlay()
231
- }
232
- }
233
- })
234
-
235
- const blockAnimatedStyle = useAnimatedStyle(() => {
236
- if (axis === 'y') {
237
- return {
238
- transform: [{translateY: translate.value}],
239
- }
240
- } else {
241
- return {
242
- transform: [{translateX: translate.value}],
243
- }
244
- }
245
- })
246
-
247
- const displayOverlayStyle = useAnimatedStyle(() => {
248
- return {
249
- display: changeDisplay.value as ViewStyle['display'],
250
- opacity: opacity.value,
251
- }
252
- })
253
-
254
- return (
255
- <>
256
- {overlay && (
257
- <AnimatedTouchableOpacity
258
- onPress={() => closeOnOverlayPress && collapse()}>
259
- <Animated.View
260
- style={[
261
- StyleSheet.absoluteFillObject,
262
- displayOverlayStyle,
263
- SlidingDrawerStyles.overlay(colorPalette),
264
- ]}
265
- />
266
- </AnimatedTouchableOpacity>
267
- )}
268
- <GestureDetector gesture={slideGesture}>
269
- <Animated.View
270
- style={[
271
- styles.container(flex, direction, axis, width),
272
- blockAnimatedStyle,
273
- SlidingDrawerStyles.slider(colorPalette),
274
- ]}>
275
- {children}
276
- </Animated.View>
277
- </GestureDetector>
278
- </>
279
- )
280
- }
281
- export default SlidingDrawer
282
-
283
- const styles = StyleSheet.create({
284
- container: (flex, direction, axis, width) => ({
285
- position: flex === 0 ? 'absolute' : 'relative',
286
- bottom: axis === 'y' && direction === -1 ? 0 : 'auto',
287
- top: axis === 'y' && direction === 1 ? 0 : 'auto',
288
- left: axis === 'x' ? 0 : 'auto',
289
- flex: flex === 0 ? 1 : flex,
290
- height: '100%',
291
- width,
292
- flexDirection: 'column',
293
- alignSelf: 'stretch',
294
- alignItems: 'center',
295
- justifyContent: 'flex-start',
296
- zIndex: 100,
297
- }),
298
- customStyle: style => ({
299
- ...style,
300
- }),
301
- })