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,426 @@
1
+ import React from 'react';
2
+ import { Children, useLayoutEffect, useRef, useState } from 'react';
3
+ import type { ReactNode } from 'react';
4
+ import { ScrollView, View } from 'react-native';
5
+ import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
6
+ import { StyleSheet, UnistylesRuntime } from 'react-native-unistyles';
7
+
8
+ export type RowProps = {
9
+ children?: ReactNode;
10
+ style?: StyleProp<ViewStyle>;
11
+ center?: boolean;
12
+ vCenter?: boolean;
13
+ hCenter?: boolean;
14
+ flex?: number;
15
+ gap?: number;
16
+ } & ViewProps;
17
+
18
+ const Row: React.FC<RowProps> = ({
19
+ children,
20
+ style,
21
+ center,
22
+ vCenter,
23
+ hCenter,
24
+ flex = 0,
25
+ gap = 0,
26
+ ...props
27
+ }) => {
28
+ return (
29
+ <View
30
+ style={[styles.row(center, vCenter, hCenter, gap, flex), style]}
31
+ {...props}
32
+ >
33
+ {children}
34
+ </View>
35
+ );
36
+ };
37
+ const Column: React.FC<RowProps> = ({
38
+ children,
39
+ style,
40
+ center,
41
+ vCenter,
42
+ hCenter,
43
+ flex = 0,
44
+ gap = 0,
45
+ ...props
46
+ }) => {
47
+ return (
48
+ <View
49
+ style={[styles.column(center, vCenter, hCenter, gap, flex), style]}
50
+ {...props}
51
+ >
52
+ {children}
53
+ </View>
54
+ );
55
+ };
56
+
57
+ export const VSpacer = ({ size }: { size?: number }) => {
58
+ return (
59
+ <View
60
+ style={{
61
+ width: '100%',
62
+ height: size ? size : 'auto',
63
+ flex: size ? 0 : 1,
64
+ }}
65
+ />
66
+ );
67
+ };
68
+ export const HSpacer = ({ size }: { size?: number }) => {
69
+ return (
70
+ <View
71
+ style={{
72
+ height: '100%',
73
+ width: size ? size : 'auto',
74
+ flex: size ? 0 : 1,
75
+ }}
76
+ />
77
+ );
78
+ };
79
+ export const FlexSpacer = () => {
80
+ return (
81
+ <View
82
+ style={{ flex: 1, width: '100%', borderWidth: 1, borderColor: 'lime' }}
83
+ />
84
+ );
85
+ };
86
+ const styles = StyleSheet.create(() => ({
87
+ row: (center, vCenter, hCenter, gap, flex) => ({
88
+ flex,
89
+ gap,
90
+ flexDirection: 'row',
91
+ alignItems: center ? 'center' : vCenter ? 'center' : 'stretch',
92
+ justifyContent: center ? 'center' : hCenter ? 'center' : 'flex-start',
93
+ }),
94
+ column: (center, vCenter, hCenter, gap, flex) => ({
95
+ flex,
96
+ gap,
97
+ flexDirection: 'column',
98
+ alignItems: center ? 'center' : vCenter ? 'center' : 'flex-start',
99
+ justifyContent: center ? 'center' : hCenter ? 'center' : 'flex-start',
100
+ }),
101
+ }));
102
+
103
+ export { Row, Column };
104
+
105
+ export const gap = (size: number) => ({
106
+ gap: size,
107
+ });
108
+
109
+ type FullHeightLayoutProps = {
110
+ style?: StyleProp<ViewStyle>;
111
+ justifyContent?:
112
+ | 'center'
113
+ | 'flex-start'
114
+ | 'flex-end'
115
+ | 'space-between'
116
+ | 'space-around'
117
+ | 'space-evenly';
118
+ alignItems?: 'center' | 'flex-start' | 'flex-end' | 'baseline' | 'stretch';
119
+ children: ReactNode;
120
+ };
121
+
122
+ export const FullHeightLayout = ({
123
+ style,
124
+ justifyContent = 'space-between',
125
+ alignItems = 'flex-start',
126
+ children,
127
+ }: FullHeightLayoutProps) => {
128
+ return (
129
+ <View
130
+ style={[
131
+ fullHeightLayoutStyles.fullHeightLayout(justifyContent, alignItems),
132
+ style,
133
+ ]}
134
+ >
135
+ {children}
136
+ </View>
137
+ );
138
+ };
139
+
140
+ /* Layout with "100%" height on portrait and scrollable in landscape
141
+ * use case: as Children of KeyboardAwareScrollView it wraps in a scroll view
142
+ * and it allows to replicate a flex: 1 full height screen column,
143
+ * with justify options and fit the screen
144
+ */
145
+ const fullHeightLayoutStyles = StyleSheet.create((_, rt) => ({
146
+ fullHeightLayout: (justifyContent, alignItems) => ({
147
+ width: '100%',
148
+ height: rt.isPortrait
149
+ ? rt.screen.height - rt.insets.top - rt.insets.bottom
150
+ : 'auto',
151
+ justifyContent,
152
+ alignItems,
153
+ }),
154
+ }));
155
+
156
+ export const FLayout = ({
157
+ // style,
158
+ justifyContent = 'space-between',
159
+ alignItems = 'flex-start',
160
+ children,
161
+ }: FullHeightLayoutProps) => {
162
+ return (
163
+ <ScrollView
164
+ contentContainerStyle={{
165
+ flexDirection: 'column',
166
+ alignItems: 'center',
167
+ }}
168
+ >
169
+ <View
170
+ style={fHeightLayoutStyles.fullHeightLayout(justifyContent, alignItems)}
171
+ >
172
+ {children}
173
+ </View>
174
+ </ScrollView>
175
+ );
176
+ };
177
+ const fHeightLayoutStyles = StyleSheet.create((_, rt) => ({
178
+ fullHeightLayout: (justifyContent, alignItems) => ({
179
+ width: '100%',
180
+ height: rt.isPortrait
181
+ ? rt.screen.height - rt.insets.top - rt.insets.bottom
182
+ : 'auto',
183
+ justifyContent,
184
+ alignItems,
185
+ }),
186
+ }));
187
+
188
+ const GridChild = ({
189
+ parentGap,
190
+ columns,
191
+ width,
192
+ children,
193
+ }: {
194
+ parentGap: number;
195
+ columns: number;
196
+ width: number;
197
+ children: ReactNode;
198
+ }) => {
199
+ if (width === 0) return null;
200
+ const flexBasis =
201
+ width / columns - ((columns - 1) / columns) * parentGap - 0.1;
202
+ return (
203
+ width > 0 && (
204
+ <View
205
+ style={[
206
+ gridChildStyles.container,
207
+ {
208
+ width: flexBasis,
209
+ },
210
+ ]}
211
+ >
212
+ {children}
213
+ </View>
214
+ )
215
+ );
216
+ };
217
+
218
+ const gridChildStyles = StyleSheet.create({
219
+ container: {
220
+ justifyContent: 'center',
221
+ alignItems: 'center',
222
+ flex: 0,
223
+ flexShrink: 1,
224
+ },
225
+ });
226
+
227
+ // type Sizes = {
228
+ // xxs: number;
229
+ // xs: number;
230
+ // sm: number;
231
+ // md: number;
232
+ // lg: number;
233
+ // xl: number;
234
+ // xxl: number;
235
+ // };
236
+
237
+ export const FlexGrid = ({
238
+ gap = 0,
239
+ columns,
240
+ flex = 0,
241
+ style,
242
+ children,
243
+ }: {
244
+ gap?: number;
245
+ columns: number | {};
246
+ flex?: number;
247
+ style?: any;
248
+ children: ReactNode;
249
+ }) => {
250
+ const viewRef = useRef(null);
251
+ const gridItems = Children.toArray(children);
252
+ const [parentWidth, setParentWidth] = useState(0);
253
+
254
+ useLayoutEffect(() => {
255
+ // TODO: fix types
256
+ // @ts-ignore
257
+ viewRef.current?.measure((x, y, width, height, pageX, pageY) => {
258
+ setParentWidth(width);
259
+ });
260
+ }, [UnistylesRuntime.breakpoint]);
261
+
262
+ let cols: any;
263
+ if (columns instanceof Object) {
264
+ // TODO: fix types
265
+ // @ts-ignore
266
+ cols = getBreakpointColumns(columns, UnistylesRuntime.breakpoint);
267
+ } else cols = columns;
268
+
269
+ return (
270
+ <View
271
+ ref={viewRef}
272
+ style={[flexGridStyles.container(gap, flex), flexGridStyles.sx(style)]}
273
+ >
274
+ {gridItems.map((Item, index) => {
275
+ return (
276
+ <GridChild
277
+ key={index}
278
+ parentGap={gap}
279
+ columns={cols}
280
+ width={parentWidth}
281
+ >
282
+ {Item}
283
+ </GridChild>
284
+ );
285
+ })}
286
+ </View>
287
+ );
288
+ };
289
+
290
+ const flexGridStyles = StyleSheet.create({
291
+ sx: (sx: any) => ({
292
+ ...sx,
293
+ }),
294
+ container: (gap, flex) => ({
295
+ flex,
296
+ gap,
297
+ flexDirection: 'row',
298
+ flexWrap: 'wrap',
299
+ width: '100%',
300
+ }),
301
+ });
302
+
303
+ // For components with flex: 1, this prevents them from taking up all the available height.
304
+ // Instead, their height will adjust based on the height of their children.
305
+ // Additionally, it can be used as a Row layout for such components.
306
+ export const Inline = ({
307
+ children,
308
+ align = 'center',
309
+ gap = 0,
310
+ }: {
311
+ children: ReactNode;
312
+ align?: 'center' | 'flex-start' | 'flex-end';
313
+ gap?: number;
314
+ }) => {
315
+ return (
316
+ <View
317
+ style={{
318
+ flexDirection: 'row',
319
+ gap,
320
+ justifyContent: align,
321
+ }}
322
+ >
323
+ {children}
324
+ </View>
325
+ );
326
+ };
327
+
328
+ export const InlineAbsolute = ({
329
+ children,
330
+ gap = 0,
331
+ }: {
332
+ children: ReactNode;
333
+ gap?: number;
334
+ }) => {
335
+ return (
336
+ <View
337
+ style={{
338
+ flexDirection: 'row',
339
+ width: '100%',
340
+ height: '100%',
341
+ backgroundColor: 'transparent',
342
+ position: 'absolute',
343
+ gap,
344
+ }}
345
+ >
346
+ {children}
347
+ </View>
348
+ );
349
+ };
350
+ export const BlockAbsolute = ({
351
+ children,
352
+ gap = 0,
353
+ }: {
354
+ children: ReactNode;
355
+ gap?: number;
356
+ }) => {
357
+ return (
358
+ <View
359
+ style={{
360
+ flexDirection: 'column',
361
+ width: '100%',
362
+ height: '100%',
363
+ backgroundColor: 'transparent',
364
+ position: 'absolute',
365
+ gap,
366
+ }}
367
+ >
368
+ {children}
369
+ </View>
370
+ );
371
+ };
372
+
373
+ const getBreakpointColumns = (
374
+ columns: Record<string, number>,
375
+ breakpoint: string
376
+ ) => {
377
+ // Define the order of breakpoints
378
+ const breakpointOrder = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
379
+
380
+ // Find the index of the current breakpoint
381
+ const currentIndex = breakpointOrder.indexOf(breakpoint);
382
+
383
+ // Traverse backwards to find the nearest available value
384
+ for (let i = currentIndex; i >= 0; i--) {
385
+ // TODO: fix types
386
+ // @ts-ignore
387
+ if (columns[breakpointOrder[i]] !== undefined) {
388
+ // @ts-ignore
389
+ return columns[breakpointOrder[i]];
390
+ }
391
+ }
392
+
393
+ // If no breakpoints match, return a default value (optional)
394
+ return undefined;
395
+ };
396
+
397
+ export const Grid = ({
398
+ cols,
399
+ children,
400
+ gap,
401
+ rowGap,
402
+ columnGap,
403
+ ...props
404
+ }: {
405
+ cols: number;
406
+ children: React.ReactNode;
407
+ gap?: number;
408
+ rowGap?: number;
409
+ columnGap?: number;
410
+ }) => {
411
+ const gridItems = React.Children.toArray(children);
412
+
413
+ const columns = Array.from({ length: cols }, (_, i) =>
414
+ gridItems.filter((_, index) => index % cols === i)
415
+ );
416
+
417
+ return (
418
+ <Row gap={gap && gap} {...props}>
419
+ {columns.map((column, index) => (
420
+ <Column style={{ flex: 1 }} key={index} gap={gap && gap}>
421
+ {column}
422
+ </Column>
423
+ ))}
424
+ </Row>
425
+ );
426
+ };
@@ -0,0 +1,68 @@
1
+ import {StyleSheet} from 'react-native-unistyles'
2
+
3
+ const BadgeStyles = StyleSheet.create(theme => ({
4
+ container: {
5
+ variants: {
6
+ colorPalette: {
7
+ primary: {
8
+ backgroundColor: 'tomato',
9
+ borderColor: 'tomato',
10
+ },
11
+ secondary: {
12
+ backgroundColor: theme.colors.foreground,
13
+ borderColor: theme.colors.categoryAquamarine,
14
+ },
15
+ },
16
+ size: {
17
+ sm: {
18
+ height: 32,
19
+ maxHeight: 32,
20
+ padding: 6,
21
+ },
22
+ md: {
23
+ maxHeight: 48,
24
+ height: 48,
25
+ padding: 8,
26
+ },
27
+ lg: {
28
+ maxHeight: 64,
29
+ height: 64,
30
+ padding: 12,
31
+ },
32
+ xl: {
33
+ maxHeight: 80,
34
+ height: 80,
35
+ padding: 16,
36
+ },
37
+ },
38
+ },
39
+ },
40
+ text: {
41
+ variants: {
42
+ colorPalette: {
43
+ primary: {
44
+ color: 'white',
45
+ },
46
+ secondary: {
47
+ color: theme.colors.categoryAquamarine,
48
+ },
49
+ },
50
+ size: {
51
+ sm: {
52
+ fontSize: 24,
53
+ },
54
+ md: {
55
+ fontSize: 28,
56
+ },
57
+ lg: {
58
+ fontSize: 32,
59
+ },
60
+ xl: {
61
+ fontSize: 36,
62
+ },
63
+ },
64
+ },
65
+ },
66
+ }))
67
+
68
+ export default BadgeStyles
@@ -0,0 +1,67 @@
1
+ import { StyleSheet } from "react-native-unistyles"
2
+
3
+ const ButtonStyles = StyleSheet.create(theme => ({
4
+ container: {
5
+ variants: {
6
+ colorPalette: {
7
+ primary: {
8
+ backgroundColor: 'tomato',
9
+ },
10
+ secondary: {
11
+ backgroundColor: theme.colors.foreground,
12
+ }
13
+ },
14
+ size: {
15
+ sm: {
16
+ height: 32,
17
+ maxHeight: 32,
18
+ padding: 6,
19
+ },
20
+ md: {
21
+ maxHeight: 48,
22
+ height: 48,
23
+ padding: 8,
24
+ },
25
+ lg: {
26
+ maxHeight: 64,
27
+ height: 64,
28
+ padding: 12,
29
+ },
30
+ xl: {
31
+ maxHeight: 80,
32
+ height: 80,
33
+ padding: 16,
34
+ }
35
+ }
36
+ },
37
+ },
38
+ text: {
39
+ variants: {
40
+ colorPalette: {
41
+ primary: {
42
+ color: 'white',
43
+ },
44
+ secondary: {
45
+ color: theme.colors.categoryAquamarine,
46
+ }
47
+ },
48
+ size: {
49
+ sm: {
50
+ fontSize: 24,
51
+ },
52
+ md: {
53
+ fontSize: 28,
54
+ },
55
+ lg: {
56
+ fontSize: 32,
57
+ },
58
+ xl: {
59
+ fontSize: 36,
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }))
65
+
66
+
67
+ export default ButtonStyles
@@ -0,0 +1,83 @@
1
+ import {StyleSheet} from 'react-native-unistyles'
2
+
3
+ export const ButtonTokens = {
4
+ defaultProps: {
5
+ colorPalette: 'primary',
6
+ size: 'md',
7
+ },
8
+ } as const
9
+ export const ButtonStyles = StyleSheet.create(theme => ({
10
+ container: {
11
+ variants: {
12
+ colorPalette: {
13
+ primary: {
14
+ backgroundColor: theme.colors.primary500,
15
+ borderColor: theme.colors.accentBackground,
16
+ },
17
+ secondary: {
18
+ backgroundColor: theme.colors.foreground,
19
+ },
20
+ },
21
+ size: {
22
+ sm: {
23
+ // height: 32,
24
+ // maxHeight: 32,
25
+ padding: 6,
26
+ borderRadius: theme.radii.sm,
27
+ borderWidth: 1,
28
+ },
29
+ md: {
30
+ // maxHeight: 48,
31
+ // height: 48,
32
+ padding: 8,
33
+ borderRadius: theme.radii.md,
34
+ borderWidth: 1,
35
+ },
36
+ lg: {
37
+ // maxHeight: 64,
38
+ // height: 64,
39
+ padding: 12,
40
+ borderRadius: theme.radii.lg,
41
+ borderWidth: 1,
42
+ },
43
+ xl: {
44
+ // maxHeight: 80,
45
+ // height: 80,
46
+ padding: 16,
47
+ borderRadius: theme.radii.xl,
48
+ borderWidth: 5,
49
+ },
50
+ },
51
+ },
52
+ },
53
+ text: {
54
+ variants: {
55
+ colorPalette: {
56
+ primary: {
57
+ color: 'white',
58
+ },
59
+ secondary: {
60
+ color: theme.colors.categoryAquamarine,
61
+ },
62
+ },
63
+ size: {
64
+ sm: {
65
+ fontSize: 24,
66
+ fontWeight: '400',
67
+ },
68
+ md: {
69
+ fontSize: 28,
70
+ fontWeight: '400',
71
+ },
72
+ lg: {
73
+ fontSize: 32,
74
+ fontWeight: '600',
75
+ },
76
+ xl: {
77
+ fontSize: 36,
78
+ fontWeight: '800',
79
+ },
80
+ },
81
+ },
82
+ },
83
+ }))
@@ -0,0 +1,42 @@
1
+ const circleProgressTokens = {
2
+ defaultProps: {
3
+ type: 'primary',
4
+ size: 'lg',
5
+ // circleSize: 'md',
6
+ },
7
+ variants: {
8
+ size: {
9
+ sm: {
10
+ trackStrokeWidth: 10,
11
+ progressStrokeWidth: 8,
12
+ size: 80,
13
+ },
14
+ md: {
15
+ trackStrokeWidth: 16,
16
+ progressStrokeWidth: 14,
17
+ size: 100,
18
+ },
19
+ lg: {
20
+ trackStrokeWidth: 20,
21
+ progressStrokeWidth: 6,
22
+ size: 100,
23
+ },
24
+ },
25
+ type: {
26
+ primary: {
27
+ trackColor: 'accentLines',
28
+ progressColor: 'categoryAquamarine',
29
+ },
30
+ secondary: {
31
+ trackColor: 'background',
32
+ progressColor: 'foreground',
33
+ },
34
+ tertiary: {
35
+ trackColor: 'background',
36
+ progressColor: 'foreground',
37
+ },
38
+ },
39
+ },
40
+ } as const
41
+
42
+ export {circleProgressTokens}
@@ -0,0 +1,38 @@
1
+ import {StyleSheet} from 'react-native-unistyles'
2
+ import {createGradientTokens} from '../style/types'
3
+
4
+ const gradientBackgroundTokens = createGradientTokens({
5
+ defaultProps: {colorPalette: 'primary', direction: 'to top left'},
6
+ variants: {
7
+ direction: {
8
+ 'to top': {x1: 0, y1: 0, x2: 0, y2: 1},
9
+ 'to bottom': {x1: 0, y1: 1, x2: 0, y2: 0},
10
+ 'to left': {x1: 0, y1: 0, x2: 1, y2: 0},
11
+ 'to right': {x1: 1, y1: 0, x2: 0, y2: 0},
12
+ 'to top left': {x1: 0, y1: 0, x2: 1, y2: 1},
13
+ 'to top right': {x1: 1, y1: 0, x2: 0, y2: 1},
14
+ 'to bottom left': {x1: 0, y1: 1, x2: 1, y2: 0},
15
+ 'to bottom right': {x1: 1, y1: 1, x2: 0, y2: 0},
16
+ },
17
+ colorPalette: {
18
+ primary: {startColor: 'blue', endColor: 'background2'},
19
+ secondary: {startColor: 'white', endColor: 'black'},
20
+ tertiary: {startColor: 'lime', endColor: 'violet'},
21
+ quaternary: {startColor: 'lime', endColor: 'tomato'},
22
+ },
23
+ },
24
+ } as const)
25
+
26
+ export {gradientBackgroundTokens}
27
+
28
+ const GradientBackgroundStyles = StyleSheet.create(theme => ({
29
+ container: {
30
+ variants: {
31
+ colorPalette: {
32
+ primary: {},
33
+ },
34
+ },
35
+ },
36
+ }))
37
+
38
+ export default GradientBackgroundStyles