@react-native-reusables/cli 0.1.2 → 0.2.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.
- package/README.md +1 -1
- package/__generated/components/ui/button.tsx +1 -1
- package/__generated/components/ui/card.tsx +1 -1
- package/__generated/components/ui/context-menu.tsx +2 -2
- package/__generated/components/ui/dropdown-menu.tsx +2 -2
- package/__generated/components/ui/hover-card.tsx +4 -4
- package/__generated/components/ui/menubar.tsx +2 -2
- package/__generated/components/ui/progress.tsx +1 -1
- package/__generated/components/ui/select.tsx +1 -1
- package/__generated/components/ui/text.tsx +1 -1
- package/__generated/components/ui/toggle-group.tsx +1 -1
- package/__generated/components/ui/typography.tsx +2 -1
- package/__generated/starter-base/README.md +16 -0
- package/__generated/starter-base/app/+not-found.tsx +18 -0
- package/__generated/starter-base/app/_layout.tsx +69 -0
- package/__generated/starter-base/app/index.tsx +95 -0
- package/__generated/starter-base/app.json +40 -0
- package/__generated/starter-base/assets/images/adaptive-icon.png +0 -0
- package/__generated/starter-base/assets/images/favicon.png +0 -0
- package/__generated/starter-base/assets/images/icon.png +0 -0
- package/__generated/starter-base/assets/images/splash.png +0 -0
- package/__generated/starter-base/babel.config.js +6 -0
- package/__generated/starter-base/components/ThemeToggle.tsx +38 -0
- package/__generated/starter-base/components/ui/avatar.tsx +45 -0
- package/__generated/starter-base/components/ui/button.tsx +88 -0
- package/__generated/starter-base/components/ui/card.tsx +57 -0
- package/__generated/starter-base/components/ui/progress.tsx +61 -0
- package/__generated/starter-base/components/ui/text.tsx +24 -0
- package/__generated/starter-base/components/ui/tooltip.tsx +39 -0
- package/__generated/starter-base/global.css +49 -0
- package/__generated/starter-base/index.js +12 -0
- package/__generated/starter-base/lib/android-navigation-bar.ts +11 -0
- package/__generated/starter-base/lib/constants.ts +18 -0
- package/__generated/starter-base/lib/icons/Info.tsx +4 -0
- package/__generated/starter-base/lib/icons/MoonStar.tsx +4 -0
- package/__generated/starter-base/lib/icons/Sun.tsx +4 -0
- package/__generated/starter-base/lib/icons/iconWithClassName.ts +14 -0
- package/__generated/starter-base/lib/useColorScheme.tsx +11 -0
- package/__generated/starter-base/lib/utils.ts +6 -0
- package/__generated/starter-base/metro.config.js +6 -0
- package/__generated/starter-base/nativewind-env.d.ts +1 -0
- package/__generated/starter-base/package.json +53 -0
- package/__generated/starter-base/tailwind.config.js +65 -0
- package/__generated/starter-base/tsconfig.json +19 -0
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npx @react-native-reusables/cli@latest add
|
|
|
14
14
|
|
|
15
15
|
### Arguments
|
|
16
16
|
|
|
17
|
-
If you do not add arguments, you will be
|
|
17
|
+
If you do not add arguments, you will be prompted to select the `ui` components you would like to add to your project.
|
|
18
18
|
|
|
19
19
|
#### UI Components
|
|
20
20
|
|
|
@@ -15,7 +15,7 @@ const buttonVariants = cva(
|
|
|
15
15
|
'border border-input bg-background web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent',
|
|
16
16
|
secondary: 'bg-secondary web:hover:opacity-80 active:opacity-80',
|
|
17
17
|
ghost: 'web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent',
|
|
18
|
-
link: 'web:underline-offset-4 web:hover:underline web:focus:underline
|
|
18
|
+
link: 'web:underline-offset-4 web:hover:underline web:focus:underline',
|
|
19
19
|
},
|
|
20
20
|
size: {
|
|
21
21
|
default: 'h-10 px-4 py-2 native:h-12 native:px-5 native:py-3',
|
|
@@ -67,7 +67,7 @@ const ContextMenuSubContent = React.forwardRef<
|
|
|
67
67
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border mt-1 border-border bg-popover p-1 shadow-md shadow-foreground/5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
68
68
|
open
|
|
69
69
|
? 'web:animate-in web:fade-in-0 web:zoom-in-95'
|
|
70
|
-
: 'web:animate-out web:fade-out-0 web:zoom-out
|
|
70
|
+
: 'web:animate-out web:fade-out-0 web:zoom-out',
|
|
71
71
|
className
|
|
72
72
|
)}
|
|
73
73
|
{...props}
|
|
@@ -93,7 +93,7 @@ const ContextMenuContent = React.forwardRef<
|
|
|
93
93
|
? StyleSheet.flatten([
|
|
94
94
|
Platform.OS !== 'web' ? StyleSheet.absoluteFill : undefined,
|
|
95
95
|
overlayStyle,
|
|
96
|
-
]
|
|
96
|
+
])
|
|
97
97
|
: Platform.OS !== 'web'
|
|
98
98
|
? StyleSheet.absoluteFill
|
|
99
99
|
: undefined
|
|
@@ -73,7 +73,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
|
|
73
73
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border mt-1 bg-popover p-1 shadow-md shadow-foreground/5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
74
74
|
open
|
|
75
75
|
? 'web:animate-in web:fade-in-0 web:zoom-in-95'
|
|
76
|
-
: 'web:animate-out web:fade-out-0 web:zoom-out
|
|
76
|
+
: 'web:animate-out web:fade-out-0 web:zoom-out',
|
|
77
77
|
className
|
|
78
78
|
)}
|
|
79
79
|
{...props}
|
|
@@ -99,7 +99,7 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
99
99
|
? StyleSheet.flatten([
|
|
100
100
|
Platform.OS !== 'web' ? StyleSheet.absoluteFill : undefined,
|
|
101
101
|
overlayStyle,
|
|
102
|
-
]
|
|
102
|
+
])
|
|
103
103
|
: Platform.OS !== 'web'
|
|
104
104
|
? StyleSheet.absoluteFill
|
|
105
105
|
: undefined
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import * as HoverCardPrimitive from '@rn-primitives/hover-card';
|
|
1
2
|
import * as React from 'react';
|
|
2
3
|
import { Platform, StyleSheet } from 'react-native';
|
|
3
4
|
import Animated, { FadeIn } from 'react-native-reanimated';
|
|
4
|
-
import { TextClassContext } from './text';
|
|
5
|
-
import * as HoverCardPrimitive from '@rn-primitives/hover-card';
|
|
6
5
|
import { cn } from '../../lib/utils';
|
|
6
|
+
import { TextClassContext } from './text';
|
|
7
7
|
|
|
8
8
|
const HoverCard = HoverCardPrimitive.Root;
|
|
9
9
|
|
|
@@ -28,8 +28,8 @@ const HoverCardContent = React.forwardRef<
|
|
|
28
28
|
className={cn(
|
|
29
29
|
'z-50 w-64 rounded-md border border-border bg-popover p-4 shadow-md shadow-foreground/5 web:outline-none web:cursor-auto data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
30
30
|
open
|
|
31
|
-
? 'web:animate-in
|
|
32
|
-
: 'web:animate-out web:fade-out-0 web:zoom-out-95
|
|
31
|
+
? 'web:animate-in web:fade-in-0 web:zoom-in-95'
|
|
32
|
+
: 'web:animate-out web:fade-out-0 web:zoom-out-95',
|
|
33
33
|
className
|
|
34
34
|
)}
|
|
35
35
|
{...props}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as MenubarPrimitive from '@rn-primitives/menubar';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { Platform, Text, TextProps, View } from 'react-native';
|
|
3
|
+
import { Platform, Text, type TextProps, View } from 'react-native';
|
|
4
4
|
import { Check } from '../../lib/icons/Check';
|
|
5
5
|
import { ChevronDown } from '../../lib/icons/ChevronDown';
|
|
6
6
|
import { ChevronRight } from '../../lib/icons/ChevronRight';
|
|
@@ -117,7 +117,7 @@ const MenubarContent = React.forwardRef<
|
|
|
117
117
|
<MenubarPrimitive.Content
|
|
118
118
|
ref={ref}
|
|
119
119
|
className={cn(
|
|
120
|
-
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 shadow-md shadow-foreground/5
|
|
120
|
+
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 shadow-md shadow-foreground/5',
|
|
121
121
|
value === itemValue
|
|
122
122
|
? 'web:animate-in web:fade-in-0 web:zoom-in-95'
|
|
123
123
|
: 'web:animate-out web:fade-out-0 web:zoom-out-95',
|
|
@@ -48,7 +48,7 @@ function Indicator({ value, className }: { value: number | undefined | null; cla
|
|
|
48
48
|
className={cn('h-full w-full flex-1 bg-primary web:transition-all', className)}
|
|
49
49
|
style={{ transform: `translateX(-${100 - (value ?? 0)}%)` }}
|
|
50
50
|
>
|
|
51
|
-
<ProgressPrimitive.Indicator className={cn('h-full w-full
|
|
51
|
+
<ProgressPrimitive.Indicator className={cn('h-full w-full', className)} />
|
|
52
52
|
</View>
|
|
53
53
|
);
|
|
54
54
|
}
|
|
@@ -76,7 +76,7 @@ const SelectContent = React.forwardRef<
|
|
|
76
76
|
return (
|
|
77
77
|
<SelectPrimitive.Portal hostName={portalHost}>
|
|
78
78
|
<SelectPrimitive.Overlay style={Platform.OS !== 'web' ? StyleSheet.absoluteFill : undefined}>
|
|
79
|
-
<Animated.View entering={FadeIn} exiting={FadeOut}>
|
|
79
|
+
<Animated.View className='z-50' entering={FadeIn} exiting={FadeOut}>
|
|
80
80
|
<SelectPrimitive.Content
|
|
81
81
|
ref={ref}
|
|
82
82
|
className={cn(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Slot from '@rn-primitives/slot';
|
|
2
|
-
import { SlottableTextProps, TextRef } from '@rn-primitives/types';
|
|
2
|
+
import type { SlottableTextProps, TextRef } from '@rn-primitives/types';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { Text as RNText } from 'react-native';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VariantProps } from 'class-variance-authority';
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
2
2
|
import type { LucideIcon } from 'lucide-react-native';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { toggleTextVariants, toggleVariants } from '../../components/ui/toggle';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Slot from '@rn-primitives/slot';
|
|
2
|
-
import { SlottableTextProps, TextRef } from '@rn-primitives/types';
|
|
2
|
+
import type { SlottableTextProps, TextRef } from '@rn-primitives/types';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { Platform, Text as RNText } from 'react-native';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
@@ -96,6 +96,7 @@ const P = React.forwardRef<TextRef, SlottableTextProps>(
|
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
);
|
|
99
|
+
|
|
99
100
|
P.displayName = 'P';
|
|
100
101
|
|
|
101
102
|
const BlockQuote = React.forwardRef<TextRef, SlottableTextProps>(
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Starter base
|
|
2
|
+
|
|
3
|
+
A starting point to help you set up your project quickly and use the common components provided by `react-native-reusables`. The idea is to make it easier for you to get started.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- NativeWind v4
|
|
8
|
+
- Dark and light mode
|
|
9
|
+
- Android Navigation Bar matches mode
|
|
10
|
+
- Persistant mode
|
|
11
|
+
- Common components
|
|
12
|
+
- ThemeToggle, Avatar, Button, Card, Progress, Text, Tooltip
|
|
13
|
+
|
|
14
|
+
<img src="https://github.com/mrzachnugent/react-native-reusables/assets/63797719/42c94108-38a7-498b-9c70-18640420f1bc"
|
|
15
|
+
alt="starter-base-template"
|
|
16
|
+
style="width:270px;" />
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Link, Stack } from 'expo-router';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { Text } from '~/components/ui/text';
|
|
4
|
+
|
|
5
|
+
export default function NotFoundScreen() {
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
<Stack.Screen options={{ title: 'Oops!' }} />
|
|
9
|
+
<View>
|
|
10
|
+
<Text>This screen doesn't exist.</Text>
|
|
11
|
+
|
|
12
|
+
<Link href='/'>
|
|
13
|
+
<Text>Go to home screen!</Text>
|
|
14
|
+
</Link>
|
|
15
|
+
</View>
|
|
16
|
+
</>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import '~/global.css';
|
|
2
|
+
|
|
3
|
+
import { DarkTheme, DefaultTheme, Theme, ThemeProvider } from '@react-navigation/native';
|
|
4
|
+
import { Stack } from 'expo-router';
|
|
5
|
+
import { StatusBar } from 'expo-status-bar';
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import { Platform } from 'react-native';
|
|
8
|
+
import { NAV_THEME } from '~/lib/constants';
|
|
9
|
+
import { useColorScheme } from '~/lib/useColorScheme';
|
|
10
|
+
import { PortalHost } from '@rn-primitives/portal';
|
|
11
|
+
import { ThemeToggle } from '~/components/ThemeToggle';
|
|
12
|
+
import { setAndroidNavigationBar } from '~/lib/android-navigation-bar';
|
|
13
|
+
|
|
14
|
+
const LIGHT_THEME: Theme = {
|
|
15
|
+
...DefaultTheme,
|
|
16
|
+
colors: NAV_THEME.light,
|
|
17
|
+
};
|
|
18
|
+
const DARK_THEME: Theme = {
|
|
19
|
+
...DarkTheme,
|
|
20
|
+
colors: NAV_THEME.dark,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
// Catch any errors thrown by the Layout component.
|
|
25
|
+
ErrorBoundary,
|
|
26
|
+
} from 'expo-router';
|
|
27
|
+
|
|
28
|
+
export default function RootLayout() {
|
|
29
|
+
const hasMounted = React.useRef(false);
|
|
30
|
+
const { colorScheme, isDarkColorScheme } = useColorScheme();
|
|
31
|
+
const [isColorSchemeLoaded, setIsColorSchemeLoaded] = React.useState(false);
|
|
32
|
+
|
|
33
|
+
useIsomorphicLayoutEffect(() => {
|
|
34
|
+
if (hasMounted.current) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (Platform.OS === 'web') {
|
|
39
|
+
// Adds the background color to the html element to prevent white background on overscroll.
|
|
40
|
+
document.documentElement.classList.add('bg-background');
|
|
41
|
+
}
|
|
42
|
+
setAndroidNavigationBar(colorScheme);
|
|
43
|
+
setIsColorSchemeLoaded(true);
|
|
44
|
+
hasMounted.current = true;
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
if (!isColorSchemeLoaded) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
|
53
|
+
<StatusBar style={isDarkColorScheme ? 'light' : 'dark'} />
|
|
54
|
+
<Stack>
|
|
55
|
+
<Stack.Screen
|
|
56
|
+
name='index'
|
|
57
|
+
options={{
|
|
58
|
+
title: 'Starter Base',
|
|
59
|
+
headerRight: () => <ThemeToggle />,
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
</Stack>
|
|
63
|
+
<PortalHost />
|
|
64
|
+
</ThemeProvider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const useIsomorphicLayoutEffect =
|
|
69
|
+
Platform.OS === 'web' && typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, { FadeInUp, FadeOutDown, LayoutAnimationConfig } from 'react-native-reanimated';
|
|
4
|
+
import { Info } from '~/lib/icons/Info';
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from '~/components/ui/avatar';
|
|
6
|
+
import { Button } from '~/components/ui/button';
|
|
7
|
+
import {
|
|
8
|
+
Card,
|
|
9
|
+
CardContent,
|
|
10
|
+
CardDescription,
|
|
11
|
+
CardFooter,
|
|
12
|
+
CardHeader,
|
|
13
|
+
CardTitle,
|
|
14
|
+
} from '~/components/ui/card';
|
|
15
|
+
import { Progress } from '~/components/ui/progress';
|
|
16
|
+
import { Text } from '~/components/ui/text';
|
|
17
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '~/components/ui/tooltip';
|
|
18
|
+
|
|
19
|
+
const GITHUB_AVATAR_URI =
|
|
20
|
+
'https://i.pinimg.com/originals/ef/a2/8d/efa28d18a04e7fa40ed49eeb0ab660db.jpg';
|
|
21
|
+
|
|
22
|
+
export default function Screen() {
|
|
23
|
+
const [progress, setProgress] = React.useState(78);
|
|
24
|
+
|
|
25
|
+
function updateProgressValue() {
|
|
26
|
+
setProgress(Math.floor(Math.random() * 100));
|
|
27
|
+
}
|
|
28
|
+
return (
|
|
29
|
+
<View className='flex-1 justify-center items-center gap-5 p-6 bg-secondary/30'>
|
|
30
|
+
<Card className='w-full max-w-sm p-6 rounded-2xl'>
|
|
31
|
+
<CardHeader className='items-center'>
|
|
32
|
+
<Avatar alt="Rick Sanchez's Avatar" className='w-24 h-24'>
|
|
33
|
+
<AvatarImage source={{ uri: GITHUB_AVATAR_URI }} />
|
|
34
|
+
<AvatarFallback>
|
|
35
|
+
<Text>RS</Text>
|
|
36
|
+
</AvatarFallback>
|
|
37
|
+
</Avatar>
|
|
38
|
+
<View className='p-3' />
|
|
39
|
+
<CardTitle className='pb-2 text-center'>Rick Sanchez</CardTitle>
|
|
40
|
+
<View className='flex-row'>
|
|
41
|
+
<CardDescription className='text-base font-semibold'>Scientist</CardDescription>
|
|
42
|
+
<Tooltip delayDuration={150}>
|
|
43
|
+
<TooltipTrigger className='px-2 pb-0.5 active:opacity-50'>
|
|
44
|
+
<Info size={14} strokeWidth={2.5} className='w-4 h-4 text-foreground/70' />
|
|
45
|
+
</TooltipTrigger>
|
|
46
|
+
<TooltipContent className='py-2 px-4 shadow'>
|
|
47
|
+
<Text className='native:text-lg'>Freelance</Text>
|
|
48
|
+
</TooltipContent>
|
|
49
|
+
</Tooltip>
|
|
50
|
+
</View>
|
|
51
|
+
</CardHeader>
|
|
52
|
+
<CardContent>
|
|
53
|
+
<View className='flex-row justify-around gap-3'>
|
|
54
|
+
<View className='items-center'>
|
|
55
|
+
<Text className='text-sm text-muted-foreground'>Dimension</Text>
|
|
56
|
+
<Text className='text-xl font-semibold'>C-137</Text>
|
|
57
|
+
</View>
|
|
58
|
+
<View className='items-center'>
|
|
59
|
+
<Text className='text-sm text-muted-foreground'>Age</Text>
|
|
60
|
+
<Text className='text-xl font-semibold'>70</Text>
|
|
61
|
+
</View>
|
|
62
|
+
<View className='items-center'>
|
|
63
|
+
<Text className='text-sm text-muted-foreground'>Species</Text>
|
|
64
|
+
<Text className='text-xl font-semibold'>Human</Text>
|
|
65
|
+
</View>
|
|
66
|
+
</View>
|
|
67
|
+
</CardContent>
|
|
68
|
+
<CardFooter className='flex-col gap-3 pb-0'>
|
|
69
|
+
<View className='flex-row items-center overflow-hidden'>
|
|
70
|
+
<Text className='text-sm text-muted-foreground'>Productivity:</Text>
|
|
71
|
+
<LayoutAnimationConfig skipEntering>
|
|
72
|
+
<Animated.View
|
|
73
|
+
key={progress}
|
|
74
|
+
entering={FadeInUp}
|
|
75
|
+
exiting={FadeOutDown}
|
|
76
|
+
className='w-11 items-center'
|
|
77
|
+
>
|
|
78
|
+
<Text className='text-sm font-bold text-sky-600'>{progress}%</Text>
|
|
79
|
+
</Animated.View>
|
|
80
|
+
</LayoutAnimationConfig>
|
|
81
|
+
</View>
|
|
82
|
+
<Progress value={progress} className='h-2' indicatorClassName='bg-sky-600' />
|
|
83
|
+
<View />
|
|
84
|
+
<Button
|
|
85
|
+
variant='outline'
|
|
86
|
+
className='shadow shadow-foreground/5'
|
|
87
|
+
onPress={updateProgressValue}
|
|
88
|
+
>
|
|
89
|
+
<Text>Update</Text>
|
|
90
|
+
</Button>
|
|
91
|
+
</CardFooter>
|
|
92
|
+
</Card>
|
|
93
|
+
</View>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"expo": {
|
|
3
|
+
"name": "Starter Base",
|
|
4
|
+
"slug": "starter-base",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"orientation": "portrait",
|
|
7
|
+
"icon": "./assets/images/icon.png",
|
|
8
|
+
"scheme": "myapp",
|
|
9
|
+
"userInterfaceStyle": "automatic",
|
|
10
|
+
"newArchEnabled": true,
|
|
11
|
+
"splash": {
|
|
12
|
+
"image": "./assets/images/splash.png",
|
|
13
|
+
"resizeMode": "contain",
|
|
14
|
+
"backgroundColor": "#ffffff"
|
|
15
|
+
},
|
|
16
|
+
"assetBundlePatterns": [
|
|
17
|
+
"**/*"
|
|
18
|
+
],
|
|
19
|
+
"ios": {
|
|
20
|
+
"supportsTablet": true
|
|
21
|
+
},
|
|
22
|
+
"android": {
|
|
23
|
+
"adaptiveIcon": {
|
|
24
|
+
"foregroundImage": "./assets/images/adaptive-icon.png",
|
|
25
|
+
"backgroundColor": "#ffffff"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"web": {
|
|
29
|
+
"bundler": "metro",
|
|
30
|
+
"output": "static",
|
|
31
|
+
"favicon": "./assets/images/favicon.png"
|
|
32
|
+
},
|
|
33
|
+
"plugins": [
|
|
34
|
+
"expo-router"
|
|
35
|
+
],
|
|
36
|
+
"experiments": {
|
|
37
|
+
"typedRoutes": true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Pressable, View } from 'react-native';
|
|
2
|
+
import { setAndroidNavigationBar } from '~/lib/android-navigation-bar';
|
|
3
|
+
import { MoonStar } from '~/lib/icons/MoonStar';
|
|
4
|
+
import { Sun } from '~/lib/icons/Sun';
|
|
5
|
+
import { useColorScheme } from '~/lib/useColorScheme';
|
|
6
|
+
import { cn } from '~/lib/utils';
|
|
7
|
+
|
|
8
|
+
export function ThemeToggle() {
|
|
9
|
+
const { isDarkColorScheme, setColorScheme } = useColorScheme();
|
|
10
|
+
|
|
11
|
+
function toggleColorScheme() {
|
|
12
|
+
const newTheme = isDarkColorScheme ? 'light' : 'dark';
|
|
13
|
+
setColorScheme(newTheme);
|
|
14
|
+
setAndroidNavigationBar(newTheme);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Pressable
|
|
19
|
+
onPress={toggleColorScheme}
|
|
20
|
+
className='web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2'
|
|
21
|
+
>
|
|
22
|
+
{({ pressed }) => (
|
|
23
|
+
<View
|
|
24
|
+
className={cn(
|
|
25
|
+
'flex-1 aspect-square pt-0.5 justify-center items-start web:px-5',
|
|
26
|
+
pressed && 'opacity-70'
|
|
27
|
+
)}
|
|
28
|
+
>
|
|
29
|
+
{isDarkColorScheme ? (
|
|
30
|
+
<MoonStar className='text-foreground' size={23} strokeWidth={1.25} />
|
|
31
|
+
) : (
|
|
32
|
+
<Sun className='text-foreground' size={24} strokeWidth={1.25} />
|
|
33
|
+
)}
|
|
34
|
+
</View>
|
|
35
|
+
)}
|
|
36
|
+
</Pressable>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as AvatarPrimitive from '@rn-primitives/avatar';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '~/lib/utils';
|
|
4
|
+
|
|
5
|
+
const AvatarPrimitiveRoot = AvatarPrimitive.Root;
|
|
6
|
+
const AvatarPrimitiveImage = AvatarPrimitive.Image;
|
|
7
|
+
const AvatarPrimitiveFallback = AvatarPrimitive.Fallback;
|
|
8
|
+
|
|
9
|
+
const Avatar = React.forwardRef<AvatarPrimitive.RootRef, AvatarPrimitive.RootProps>(
|
|
10
|
+
({ className, ...props }, ref) => (
|
|
11
|
+
<AvatarPrimitiveRoot
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
Avatar.displayName = AvatarPrimitiveRoot.displayName;
|
|
19
|
+
|
|
20
|
+
const AvatarImage = React.forwardRef<AvatarPrimitive.ImageRef, AvatarPrimitive.ImageProps>(
|
|
21
|
+
({ className, ...props }, ref) => (
|
|
22
|
+
<AvatarPrimitiveImage
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn('aspect-square h-full w-full', className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
AvatarImage.displayName = AvatarPrimitiveImage.displayName;
|
|
30
|
+
|
|
31
|
+
const AvatarFallback = React.forwardRef<AvatarPrimitive.FallbackRef, AvatarPrimitive.FallbackProps>(
|
|
32
|
+
({ className, ...props }, ref) => (
|
|
33
|
+
<AvatarPrimitiveFallback
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={cn(
|
|
36
|
+
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
AvatarFallback.displayName = AvatarPrimitiveFallback.displayName;
|
|
44
|
+
|
|
45
|
+
export { Avatar, AvatarFallback, AvatarImage };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Pressable } from 'react-native';
|
|
4
|
+
import { TextClassContext } from '~/components/ui/text';
|
|
5
|
+
import { cn } from '~/lib/utils';
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
'group flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-primary web:hover:opacity-90 active:opacity-90',
|
|
13
|
+
destructive: 'bg-destructive web:hover:opacity-90 active:opacity-90',
|
|
14
|
+
outline:
|
|
15
|
+
'border border-input bg-background web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent',
|
|
16
|
+
secondary: 'bg-secondary web:hover:opacity-80 active:opacity-80',
|
|
17
|
+
ghost: 'web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent',
|
|
18
|
+
link: 'web:underline-offset-4 web:hover:underline web:focus:underline ',
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
default: 'h-10 px-4 py-2 native:h-12 native:px-5 native:py-3',
|
|
22
|
+
sm: 'h-9 rounded-md px-3',
|
|
23
|
+
lg: 'h-11 rounded-md px-8 native:h-14',
|
|
24
|
+
icon: 'h-10 w-10',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
variant: 'default',
|
|
29
|
+
size: 'default',
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const buttonTextVariants = cva(
|
|
35
|
+
'web:whitespace-nowrap text-sm native:text-base font-medium text-foreground web:transition-colors',
|
|
36
|
+
{
|
|
37
|
+
variants: {
|
|
38
|
+
variant: {
|
|
39
|
+
default: 'text-primary-foreground',
|
|
40
|
+
destructive: 'text-destructive-foreground',
|
|
41
|
+
outline: 'group-active:text-accent-foreground',
|
|
42
|
+
secondary: 'text-secondary-foreground group-active:text-secondary-foreground',
|
|
43
|
+
ghost: 'group-active:text-accent-foreground',
|
|
44
|
+
link: 'text-primary group-active:underline',
|
|
45
|
+
},
|
|
46
|
+
size: {
|
|
47
|
+
default: '',
|
|
48
|
+
sm: '',
|
|
49
|
+
lg: 'native:text-lg',
|
|
50
|
+
icon: '',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: {
|
|
54
|
+
variant: 'default',
|
|
55
|
+
size: 'default',
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
type ButtonProps = React.ComponentPropsWithoutRef<typeof Pressable> &
|
|
61
|
+
VariantProps<typeof buttonVariants>;
|
|
62
|
+
|
|
63
|
+
const Button = React.forwardRef<React.ElementRef<typeof Pressable>, ButtonProps>(
|
|
64
|
+
({ className, variant, size, ...props }, ref) => {
|
|
65
|
+
return (
|
|
66
|
+
<TextClassContext.Provider
|
|
67
|
+
value={cn(
|
|
68
|
+
props.disabled && 'web:pointer-events-none',
|
|
69
|
+
buttonTextVariants({ variant, size })
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
<Pressable
|
|
73
|
+
className={cn(
|
|
74
|
+
props.disabled && 'opacity-50 web:pointer-events-none',
|
|
75
|
+
buttonVariants({ variant, size, className })
|
|
76
|
+
)}
|
|
77
|
+
ref={ref}
|
|
78
|
+
role='button'
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
</TextClassContext.Provider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
Button.displayName = 'Button';
|
|
86
|
+
|
|
87
|
+
export { Button, buttonTextVariants, buttonVariants };
|
|
88
|
+
export type { ButtonProps };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { TextRef, ViewRef } from '@rn-primitives/types';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Text, TextProps, View, ViewProps } from 'react-native';
|
|
4
|
+
import { TextClassContext } from '~/components/ui/text';
|
|
5
|
+
import { cn } from '~/lib/utils';
|
|
6
|
+
|
|
7
|
+
const Card = React.forwardRef<ViewRef, ViewProps>(({ className, ...props }, ref) => (
|
|
8
|
+
<View
|
|
9
|
+
ref={ref}
|
|
10
|
+
className={cn(
|
|
11
|
+
'rounded-lg border border-border bg-card shadow-sm shadow-foreground/10',
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
));
|
|
17
|
+
Card.displayName = 'Card';
|
|
18
|
+
|
|
19
|
+
const CardHeader = React.forwardRef<ViewRef, ViewProps>(({ className, ...props }, ref) => (
|
|
20
|
+
<View ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
|
21
|
+
));
|
|
22
|
+
CardHeader.displayName = 'CardHeader';
|
|
23
|
+
|
|
24
|
+
const CardTitle = React.forwardRef<TextRef, React.ComponentPropsWithoutRef<typeof Text>>(
|
|
25
|
+
({ className, ...props }, ref) => (
|
|
26
|
+
<Text
|
|
27
|
+
role='heading'
|
|
28
|
+
aria-level={3}
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
'text-2xl text-card-foreground font-semibold leading-none tracking-tight',
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
CardTitle.displayName = 'CardTitle';
|
|
39
|
+
|
|
40
|
+
const CardDescription = React.forwardRef<TextRef, TextProps>(({ className, ...props }, ref) => (
|
|
41
|
+
<Text ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
|
|
42
|
+
));
|
|
43
|
+
CardDescription.displayName = 'CardDescription';
|
|
44
|
+
|
|
45
|
+
const CardContent = React.forwardRef<ViewRef, ViewProps>(({ className, ...props }, ref) => (
|
|
46
|
+
<TextClassContext.Provider value='text-card-foreground'>
|
|
47
|
+
<View ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
|
48
|
+
</TextClassContext.Provider>
|
|
49
|
+
));
|
|
50
|
+
CardContent.displayName = 'CardContent';
|
|
51
|
+
|
|
52
|
+
const CardFooter = React.forwardRef<ViewRef, ViewProps>(({ className, ...props }, ref) => (
|
|
53
|
+
<View ref={ref} className={cn('flex flex-row items-center p-6 pt-0', className)} {...props} />
|
|
54
|
+
));
|
|
55
|
+
CardFooter.displayName = 'CardFooter';
|
|
56
|
+
|
|
57
|
+
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|