react-native-varia 0.0.1

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 (44) hide show
  1. package/README.md +53 -0
  2. package/bin/cli.js +200 -0
  3. package/lib/components/Badge.tsx +96 -0
  4. package/lib/components/Button.tsx +131 -0
  5. package/lib/components/CircleProgress.tsx +180 -0
  6. package/lib/components/Divider.tsx +43 -0
  7. package/lib/components/GradientBackground.tsx +68 -0
  8. package/lib/components/GradientText.tsx +121 -0
  9. package/lib/components/Icon.tsx +13 -0
  10. package/lib/components/IconWrapper.tsx +109 -0
  11. package/lib/components/Input.tsx +120 -0
  12. package/lib/components/Link.tsx +87 -0
  13. package/lib/components/Modal.tsx +157 -0
  14. package/lib/components/ReText.tsx +124 -0
  15. package/lib/components/Slider.tsx +334 -0
  16. package/lib/components/Slideshow.tsx +317 -0
  17. package/lib/components/SlidingDrawer.tsx +307 -0
  18. package/lib/components/Spinner.tsx +44 -0
  19. package/lib/components/Switch.tsx +224 -0
  20. package/lib/components/Text.tsx +107 -0
  21. package/lib/components/index.tsx +83 -0
  22. package/lib/mixins.ts +180 -0
  23. package/lib/patterns/index.tsx +426 -0
  24. package/lib/theme/Badge.recipe.tsx +68 -0
  25. package/lib/theme/Button.recipe-old.tsx +67 -0
  26. package/lib/theme/Button.recipe.tsx +83 -0
  27. package/lib/theme/CircleProgress.recipe.tsx +42 -0
  28. package/lib/theme/GradientBackground.recipe.tsx +38 -0
  29. package/lib/theme/GradientText.recipe.tsx +49 -0
  30. package/lib/theme/Icon.recipe.tsx +122 -0
  31. package/lib/theme/IconWrapper.recipe.tsx +121 -0
  32. package/lib/theme/Input.recipe.tsx +110 -0
  33. package/lib/theme/Link.recipe.tsx +51 -0
  34. package/lib/theme/Modal.recipe.tsx +34 -0
  35. package/lib/theme/ReText.recipe.tsx +7 -0
  36. package/lib/theme/Slider.recipe.tsx +226 -0
  37. package/lib/theme/Slideshow.recipe.tsx +142 -0
  38. package/lib/theme/SlidingDrawer.recipe.tsx +91 -0
  39. package/lib/theme/Spinner.recipe.tsx +8 -0
  40. package/lib/theme/Switch.recipe.tsx +70 -0
  41. package/lib/theme/Text.recipe.tsx +10 -0
  42. package/lib/types.ts +70 -0
  43. package/lib/utils.ts +80 -0
  44. package/package.json +18 -0
@@ -0,0 +1,124 @@
1
+ import type { StyleProp, TextInputProps, TextStyle } from 'react-native';
2
+ import { TextInput } from 'react-native';
3
+ import Animated, { useAnimatedProps } from 'react-native-reanimated';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { TextSemanticSizes } from '../style/types';
6
+ import reTextTokens from '../theme/ReText.recipe';
7
+
8
+ Animated.addWhitelistedNativeProps({ text: true });
9
+
10
+ export interface TextProps extends Omit<TextInputProps, 'value' | 'style'> {
11
+ text: Animated.SharedValue<string>;
12
+ as?: TextSemanticSizes;
13
+ fontSize?: number | undefined;
14
+ color?: string | undefined;
15
+ style?: TextStyle;
16
+ children?: React.ReactNode;
17
+ numberOfLines?: number;
18
+ adjustsFontSizeToFit?: boolean;
19
+ // style?: Animated.AnimateProps<RNTextProps>['style'];
20
+ mixins?: StyleProp<TextStyle> | StyleProp<TextStyle>[];
21
+ }
22
+
23
+ const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
24
+
25
+ const ReText = ({
26
+ as = 'body',
27
+ fontSize,
28
+ color,
29
+ text,
30
+ style,
31
+ mixins,
32
+ ...rest
33
+ }: TextProps) => {
34
+
35
+ styles.useVariants({
36
+ as,
37
+ });
38
+
39
+ const animatedProps = useAnimatedProps(() => {
40
+ return {
41
+ text: text.value,
42
+ } as any;
43
+ });
44
+ return (
45
+ <AnimatedTextInput
46
+ underlineColorAndroid="transparent"
47
+ multiline={true}
48
+ editable={false}
49
+ value={text.value}
50
+ style={[
51
+ styles.text(fontSize, color, reTextTokens.defaultProps),
52
+ style,
53
+ mixins && mixins,
54
+ ]}
55
+ onStartShouldSetResponder={() => true}
56
+ {...rest}
57
+ {...{ animatedProps }}
58
+ />
59
+ );
60
+ };
61
+
62
+ const styles = StyleSheet.create((theme) => ({
63
+ text: (
64
+ fontSize: number | undefined,
65
+ color: string | undefined,
66
+ defaultProps
67
+ ) => ({
68
+ verticalAlign: 'bottom',
69
+ padding: 0,
70
+ //@ts-ignore
71
+ fontSize: theme.fontSizes.xxl,
72
+ textAlign: 'left',
73
+ color: color || defaultProps.color,
74
+ variants: {
75
+ as: {
76
+ h1: {
77
+ //@ts-ignore
78
+ fontSize: fontSize || theme.fontSizes.xxl,
79
+ },
80
+ h2: {
81
+ //@ts-ignore
82
+ fontSize: fontSize || theme.fontSizes.xl,
83
+ },
84
+ h3: {
85
+ //@ts-ignore
86
+ fontSize: fontSize || theme.fontSizes.lg,
87
+ },
88
+ h4: {
89
+ //@ts-ignore
90
+ fontSize: fontSize || theme.fontSizes.md,
91
+ },
92
+ h5: {
93
+ //@ts-ignore
94
+ fontSize: fontSize || theme.fontSizes.sm,
95
+ },
96
+ h6: {
97
+ //@ts-ignore
98
+ fontSize: fontSize || theme.fontSizes.xs,
99
+ },
100
+ h7: {
101
+ //@ts-ignore
102
+ fontSize: fontSize || theme.fontSizes.xs,
103
+ },
104
+ h8: {
105
+ //@ts-ignore
106
+ fontSize: fontSize || theme.fontSizes.xxs,
107
+ },
108
+ h9: {
109
+ //@ts-ignore
110
+ fontSize: fontSize || theme.fontSizes.xxs,
111
+ },
112
+ body: {
113
+ //@ts-ignore
114
+ fontSize: fontSize || theme.fontSizes.md,
115
+ },
116
+ },
117
+ },
118
+ }),
119
+ customStyle: (style) => ({
120
+ ...style,
121
+ }),
122
+ }));
123
+
124
+ export default ReText;
@@ -0,0 +1,334 @@
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 {hexToRgba} from '../style/utils'
11
+ import {SliderStyles, SliderTokens} from '../theme/Slider.recipe'
12
+
13
+ type SliderVariants = UnistylesVariants<typeof SliderStyles>
14
+
15
+ type SliderProps = SliderVariants & {
16
+ thickness?: number
17
+ minimumTrackThickness?: number
18
+ minimumTrackColor?: string
19
+ displayStepsOnMinimumTrack?: boolean
20
+ thumbSize?: {
21
+ width: number
22
+ height: number
23
+ }
24
+ thumbChildren?: React.ReactNode
25
+ axis: 'x' | 'y'
26
+ steps?: number
27
+ value?: number
28
+ onValueChange?: (value: number) => void
29
+ onSlidingComplete?: (value: number) => void
30
+ }
31
+
32
+ const Slider = ({
33
+ size = SliderTokens.defaultProps.size,
34
+ colorPalette = SliderTokens.defaultProps.colorPalette,
35
+ thickness = 10,
36
+ minimumTrackColor = '#FFF',
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
+ colorPalette,
54
+ })
55
+ const halfSize = (SliderTokens.thumb.variants.size[size]?.width ?? 0) / 2
56
+ const thumbSize = {
57
+ width: SliderTokens.thumb.variants.size[size]?.width,
58
+ height: SliderTokens.thumb.variants.size[size]?.height,
59
+ }
60
+ const maximumTrackWidth =
61
+ SliderTokens.maximumTrack.variants.size[size]?.height ?? 0
62
+ const minimumTrackWidth =
63
+ SliderTokens.minimumTrack.variants.size[size]?.height ?? 0
64
+ const context = useSharedValue({x: 0})
65
+ const translate = useSharedValue(value)
66
+ const trackLength = useSharedValue(0)
67
+ const isInsideChild = useSharedValue(false)
68
+
69
+ const handleTrackLayout = (event: LayoutChangeEvent) => {
70
+ const {width, height} = event.nativeEvent.layout
71
+ trackLength.value = axis === 'x' ? width : height
72
+ }
73
+
74
+ const slideGesture = Gesture.Pan()
75
+ .onTouchesDown(() => {
76
+ // Mark the child gesture as active when touching the child zone
77
+ isInsideChild.value = true
78
+ })
79
+ .onBegin(() => {
80
+ context.value.x = translate.value
81
+ })
82
+ .onUpdate(e => {
83
+ const stepLength = steps ? trackLength.value / steps : 1
84
+ const delta = axis === 'y' ? -e.translationY : e.translationX
85
+
86
+ const rawValue = Math.max(
87
+ 0,
88
+ Math.min(context.value.x + delta, trackLength.value),
89
+ )
90
+
91
+ const snappedValue = steps
92
+ ? Math.round(rawValue / stepLength) * stepLength
93
+ : rawValue
94
+
95
+ translate.value = snappedValue
96
+
97
+ if (steps) {
98
+ const stepIndex = Math.round(snappedValue / stepLength)
99
+ onValueChange && runOnJS(onValueChange)(stepIndex)
100
+ } else {
101
+ onValueChange &&
102
+ runOnJS(onValueChange)(
103
+ Math.round((snappedValue / trackLength.value) * 100) / 100,
104
+ )
105
+ }
106
+ })
107
+ .onEnd(() => {
108
+ const stepLength = steps ? trackLength.value / steps : 1
109
+
110
+ const snappedValue = steps
111
+ ? Math.round(translate.value / stepLength)
112
+ : translate.value / trackLength.value
113
+
114
+ onSlidingComplete && runOnJS(onSlidingComplete)(snappedValue)
115
+ })
116
+ .onFinalize(() => {
117
+ isInsideChild.value = false
118
+ })
119
+ // New Tap Gesture
120
+ const tapGesture = Gesture.Tap()
121
+ .onBegin(e => {
122
+ if (isInsideChild.value) {
123
+ return // Prevent parent gesture in child zone
124
+ }
125
+ const tapPosition = axis === 'x' ? e.x : e.y
126
+ const stepLength = steps ? trackLength.value / steps : 1
127
+
128
+ // Calculate the raw position
129
+ const rawValue =
130
+ axis === 'y'
131
+ ? Math.max(
132
+ 0,
133
+ Math.min(trackLength.value - tapPosition, trackLength.value),
134
+ ) // Invert for "y"
135
+ : Math.max(0, Math.min(tapPosition, trackLength.value))
136
+
137
+ // Snap to step if needed
138
+ const snappedValue = steps
139
+ ? Math.round(rawValue / stepLength) * stepLength
140
+ : rawValue
141
+
142
+ translate.value = snappedValue
143
+
144
+ if (steps) {
145
+ const stepIndex = Math.round(snappedValue / stepLength)
146
+ onValueChange && runOnJS(onValueChange)(stepIndex)
147
+ onSlidingComplete && runOnJS(onSlidingComplete)(stepIndex)
148
+ } else {
149
+ const normalizedValue =
150
+ Math.round((snappedValue / trackLength.value) * 100) / 100
151
+ onValueChange && runOnJS(onValueChange)(normalizedValue)
152
+ onSlidingComplete && runOnJS(onSlidingComplete)(normalizedValue)
153
+ }
154
+ })
155
+ .simultaneousWithExternalGesture(slideGesture)
156
+
157
+ const animatedTrack = useAnimatedStyle(() => ({
158
+ [axis === 'y' ? 'height' : 'width']: translate.value + halfSize,
159
+ }))
160
+
161
+ const opacityTrack = steps !== undefined && displayStepsOnMinimumTrack
162
+ console.log('🚀 ~ opacityTrack:', opacityTrack)
163
+
164
+ // const minimumTrackBackgroundColor =
165
+ // SliderTokens.minimumTrack.variants.colorPalette[colorPalette]
166
+ // ?.backgroundColor;
167
+ const minimumTrackBackgroundColor = hexToRgba('#ff0000', 0.5)
168
+ console.log('🚀 ~ minimumTrackBackgroundColor:', minimumTrackBackgroundColor)
169
+
170
+ return (
171
+ // <View style={{flexDirection: 'row', flex: 1}}>
172
+ <View
173
+ style={[
174
+ styles.container(maximumTrackWidth, halfSize),
175
+ SliderStyles.container,
176
+ ]}>
177
+ {axis === 'x' && <View style={{width: halfSize}} />}
178
+ <GestureDetector gesture={tapGesture}>
179
+ <View
180
+ style={[
181
+ styles.maximumTrack(maximumTrackWidth),
182
+ SliderStyles.maximumTrack,
183
+ ]}
184
+ onLayout={handleTrackLayout}>
185
+ {steps && (
186
+ <View style={styles.steps}>
187
+ {Array.from({length: steps + 1}, (_, index) => index).map(
188
+ (_, i) => (
189
+ <View
190
+ key={i}
191
+ style={[styles.step(i, steps), SliderStyles.step]}
192
+ />
193
+ ),
194
+ )}
195
+ </View>
196
+ )}
197
+ <Animated.View
198
+ style={[
199
+ styles.minimumTrack(halfSize, minimumTrackWidth),
200
+ SliderStyles.minimumTrack(opacityTrack),
201
+ animatedTrack,
202
+ ]}>
203
+ <GestureDetector gesture={slideGesture}>
204
+ <View
205
+ style={[SliderStyles.thumb, styles.thumb(halfSize, thumbSize)]}>
206
+ {ThumbChildren || null}
207
+ </View>
208
+ </GestureDetector>
209
+ </Animated.View>
210
+ </View>
211
+ </GestureDetector>
212
+ {axis === 'y' && <View style={{height: halfSize}} />}
213
+ </View>
214
+ )
215
+ }
216
+
217
+ const stylesX = StyleSheet.create(theme => ({
218
+ container: (thickness, halfSize) => ({
219
+ flex: thickness ? 1 : 1,
220
+ maxWidth: 'auto',
221
+ borderRadius: 22,
222
+ width: '100%',
223
+ height: '100%',
224
+ paddingTop: 0,
225
+ paddingRight: halfSize,
226
+ flexDirection: 'row',
227
+ }),
228
+ maximumTrack: thickness => ({
229
+ flex: thickness ? 1 : 1,
230
+ justifyContent: 'center',
231
+ position: 'relative',
232
+ borderRadius: 22,
233
+ }),
234
+ minimumTrack: (halfSize, minimumTrackThickness) => ({
235
+ height: minimumTrackThickness,
236
+ width: '100%',
237
+ left: halfSize * -1,
238
+ bottom: 'auto',
239
+ paddingBottom: 0,
240
+ paddingLeft: halfSize,
241
+ borderBottomLeftRadius: 20,
242
+ borderBottomRightRadius: 0,
243
+ borderTopLeftRadius: 20,
244
+ justifyContent: 'center',
245
+ }),
246
+ thumb: (halfSize, thumbSize) => ({
247
+ width: halfSize ? thumbSize.width : thumbSize.width,
248
+ height: thumbSize.height,
249
+ borderRadius: 25,
250
+ position: 'absolute',
251
+ right: 0,
252
+ zIndex: 2,
253
+ transform: [{translateX: '50%'}],
254
+ }),
255
+ steps: {
256
+ width: '100%',
257
+ height: '100%',
258
+ flexDirection: 'row',
259
+ justifyContent: 'space-between',
260
+ alignItems: 'center',
261
+ zIndex: 0,
262
+ position: 'absolute',
263
+ },
264
+ step: (index, length) => ({
265
+ width: 1,
266
+ height: '100%',
267
+ backgroundColor:
268
+ index === 0 || index === length ? 'transparent' : theme.colors.background,
269
+ }),
270
+ }))
271
+ const stylesY = StyleSheet.create(theme => ({
272
+ container: (maximumTrackWidth, halfSize) => ({
273
+ // borderWidth: 1,
274
+ // borderColor: theme.colors.foreground,
275
+ flex: 1,
276
+ maxWidth: maximumTrackWidth,
277
+ maxHeight: 'auto',
278
+ borderRadius: 22,
279
+ width: '100%',
280
+ height: '100%',
281
+ paddingTop: halfSize,
282
+ paddingRight: 0,
283
+ backgroundColor: theme.colors.background,
284
+ flexDirection: 'column',
285
+ }),
286
+ maximumTrack: maximumTrackWidth => ({
287
+ flex: 1,
288
+ borderRadius: 22,
289
+ backgroundColor: theme.colors.background,
290
+ justifyContent: 'center',
291
+ alignItems: 'center',
292
+ position: 'relative',
293
+ maxWidth: maximumTrackWidth,
294
+ }),
295
+ minimumTrack: (halfSize, minimumTrackWidth) => ({
296
+ position: 'absolute',
297
+ height: '100%',
298
+ width: minimumTrackWidth,
299
+ left: 'auto',
300
+ bottom: halfSize * -1,
301
+ paddingBottom: halfSize,
302
+ paddingLeft: 0,
303
+ borderBottomLeftRadius: 20,
304
+ borderBottomRightRadius: 20,
305
+ borderTopLeftRadius: 0,
306
+ alignItems: 'center',
307
+ }),
308
+ thumb: (halfSize, thumbSize) => ({
309
+ borderRadius: 25,
310
+ backgroundColor: theme.colors.foreground,
311
+ position: 'absolute',
312
+ top: 0,
313
+ zIndex: 2,
314
+ transform: [{translateY: halfSize * -1}],
315
+ width: thumbSize.height,
316
+ height: thumbSize.width,
317
+ }),
318
+ steps: {
319
+ width: '100%',
320
+ height: '100%',
321
+ flexDirection: 'column',
322
+ justifyContent: 'space-between',
323
+ alignItems: 'center',
324
+ zIndex: 0,
325
+ },
326
+ step: (index, length) => ({
327
+ width: '100%',
328
+ height: 1,
329
+ backgroundColor:
330
+ index === 0 || index === length ? 'transparent' : theme.colors.foreground,
331
+ }),
332
+ }))
333
+
334
+ export default Slider