create-expo-stack 2.6.5 → 2.7.0-next.086c9e7

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 (47) hide show
  1. package/README.md +23 -9
  2. package/build/commands/create-expo-stack.js +62 -15
  3. package/build/templates/base/App.tsx.ejs +3 -0
  4. package/build/templates/base/app.json.ejs +5 -1
  5. package/build/templates/base/babel.config.js.ejs +1 -1
  6. package/build/templates/base/package.json.ejs +42 -3
  7. package/build/templates/base/prettier.config.js.ejs +1 -1
  8. package/build/templates/base/tsconfig.json.ejs +10 -6
  9. package/build/templates/packages/expo-router/drawer/app/_layout.tsx.ejs +4 -1
  10. package/build/templates/packages/expo-router/stack/app/_layout.tsx.ejs +4 -1
  11. package/build/templates/packages/expo-router/tabs/app/_layout.tsx.ejs +4 -1
  12. package/build/templates/packages/nativewindui/app/+not-found.tsx.ejs +18 -0
  13. package/build/templates/packages/nativewindui/app/_layout.tsx.ejs +91 -0
  14. package/build/templates/packages/nativewindui/app/index.tsx.ejs +707 -0
  15. package/build/templates/packages/nativewindui/app/modal.tsx.ejs +33 -0
  16. package/build/templates/packages/nativewindui/components/nativewind-ui/ActivityIndicator.tsx.ejs +10 -0
  17. package/build/templates/packages/nativewindui/components/nativewind-ui/Avatar.tsx.ejs +139 -0
  18. package/build/templates/packages/nativewindui/components/nativewind-ui/DatePicker.android.tsx.ejs +66 -0
  19. package/build/templates/packages/nativewindui/components/nativewind-ui/DatePicker.tsx.ejs +10 -0
  20. package/build/templates/packages/nativewindui/components/nativewind-ui/Picker.tsx.ejs +39 -0
  21. package/build/templates/packages/nativewindui/components/nativewind-ui/ProgressIndicator.tsx.ejs +95 -0
  22. package/build/templates/packages/nativewindui/components/nativewind-ui/SegmentedControl.tsx.ejs +22 -0
  23. package/build/templates/packages/nativewindui/components/nativewind-ui/Sheet.tsx.ejs +59 -0
  24. package/build/templates/packages/nativewindui/components/nativewind-ui/Slider.tsx.ejs +28 -0
  25. package/build/templates/packages/nativewindui/components/nativewind-ui/Text.tsx.ejs +79 -0
  26. package/build/templates/packages/nativewindui/components/nativewind-ui/ThemeToggle.tsx.ejs +37 -0
  27. package/build/templates/packages/nativewindui/components/nativewind-ui/Toggle.tsx.ejs +17 -0
  28. package/build/templates/packages/nativewindui/expo-env.d.ts.ejs +3 -0
  29. package/build/templates/packages/nativewindui/global.css.ejs +91 -0
  30. package/build/templates/packages/nativewindui/lib/cn.ts.ejs +6 -0
  31. package/build/templates/packages/nativewindui/lib/useColorScheme.tsx.ejs +14 -0
  32. package/build/templates/packages/nativewindui/lib/useHeaderSearchBar.tsx.ejs +31 -0
  33. package/build/templates/packages/nativewindui/metro.config.js.ejs +10 -0
  34. package/build/templates/packages/nativewindui/nativewind-env.d.ts.ejs +1 -0
  35. package/build/templates/packages/nativewindui/tailwind.config.js.ejs +66 -0
  36. package/build/templates/packages/nativewindui/theme/colors.ts.ejs +71 -0
  37. package/build/templates/packages/nativewindui/theme/index.ts.ejs +29 -0
  38. package/build/templates/packages/react-navigation/App.tsx.ejs +4 -1
  39. package/build/types/types.d.ts +4 -2
  40. package/build/types/utilities/clearNavigationPackages.d.ts +2 -0
  41. package/build/types.js +2 -1
  42. package/build/utilities/clearNavigationPackages.js +11 -0
  43. package/build/utilities/configureProjectFiles.js +264 -196
  44. package/build/utilities/generateProjectFiles.js +18 -13
  45. package/build/utilities/printOutput.js +5 -5
  46. package/build/utilities/runCLI.js +99 -22
  47. package/package.json +67 -67
@@ -0,0 +1,33 @@
1
+ import { Icon } from '@roninoss/icons';
2
+ import { StatusBar } from 'expo-status-bar';
3
+ import { Linking, Platform, View } from 'react-native';
4
+
5
+ import { Text } from '~/components/nativewind-ui/Text';
6
+ import { useColorScheme } from '~/lib/useColorScheme';
7
+
8
+ export default function ModalScreen() {
9
+ const { colors, colorScheme } = useColorScheme();
10
+ return (
11
+ <>
12
+ <StatusBar
13
+ style={Platform.OS === 'ios' ? 'light' : colorScheme === 'dark' ? 'light' : 'dark'}
14
+ />
15
+ <View className="flex-1 items-center justify-center gap-1 px-12">
16
+ <Icon name="file-plus-outline" size={42} color={colors.grey} />
17
+ <Text variant="title3" className="pb-1 text-center font-semibold">
18
+ NativeWindUI
19
+ </Text>
20
+ <Text color="tertiary" variant="subhead" className="pb-4 text-center">
21
+ You can install any of the free components from the{' '}
22
+ <Text
23
+ onPress={() => Linking.openURL('https://nativewindui.com')}
24
+ variant="subhead"
25
+ className="text-primary">
26
+ NativeWindUI
27
+ </Text>
28
+ {' website.'}
29
+ </Text>
30
+ </View>
31
+ </>
32
+ );
33
+ }
@@ -0,0 +1,10 @@
1
+ import { ActivityIndicator as RNActivityIndicator } from 'react-native';
2
+
3
+ import { useColorScheme } from '~/lib/useColorScheme';
4
+
5
+ export function ActivityIndicator(
6
+ props: React.ComponentPropsWithoutRef<typeof RNActivityIndicator>
7
+ ) {
8
+ const { colors } = useColorScheme();
9
+ return <RNActivityIndicator color={colors.primary} {...props} />;
10
+ }
@@ -0,0 +1,139 @@
1
+ import * as React from 'react';
2
+ import {
3
+ ImageErrorEventData,
4
+ ImageLoadEventData,
5
+ NativeSyntheticEvent,
6
+ Image as RNImage,
7
+ View,
8
+ } from 'react-native';
9
+
10
+ import { cn } from '~/lib/cn';
11
+
12
+ interface AvatarRootProps {
13
+ alt: string;
14
+ }
15
+
16
+ interface AvatarImageProps {
17
+ children?: React.ReactNode;
18
+ onLoadingStatusChange?: (status: 'error' | 'loaded') => void;
19
+ }
20
+
21
+ type AvatarState = 'loading' | 'error' | 'loaded';
22
+
23
+ interface IRootContext extends AvatarRootProps {
24
+ status: AvatarState;
25
+ setStatus: (status: AvatarState) => void;
26
+ }
27
+
28
+ const RootContext = React.createContext<IRootContext | null>(null);
29
+
30
+ const Avatar = React.forwardRef<
31
+ React.ElementRef<typeof View>,
32
+ React.ComponentPropsWithoutRef<typeof View> & AvatarRootProps
33
+ >(({ alt, className, ...viewProps }, ref) => {
34
+ const [status, setStatus] = React.useState<AvatarState>('loading');
35
+ return (
36
+ <RootContext.Provider value={{ alt, status, setStatus }}>
37
+ <View
38
+ ref={ref}
39
+ className={cn(
40
+ 'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
41
+ className
42
+ )}
43
+ {...viewProps}
44
+ />
45
+ </RootContext.Provider>
46
+ );
47
+ });
48
+
49
+ Avatar.displayName = 'Avatar';
50
+
51
+ function useRootContext() {
52
+ const context = React.useContext(RootContext);
53
+ if (!context) {
54
+ throw new Error(
55
+ 'Avatar compound components cannot be rendered outside the Avatar component'
56
+ );
57
+ }
58
+ return context;
59
+ }
60
+
61
+ const AvatarImage = React.forwardRef<
62
+ React.ElementRef<typeof RNImage>,
63
+ Omit<React.ComponentPropsWithoutRef<typeof RNImage>, 'alt'> & AvatarImageProps
64
+ >(
65
+ (
66
+ {
67
+ onLoad: onLoadProps,
68
+ onError: onErrorProps,
69
+ onLoadingStatusChange,
70
+ className,
71
+ ...props
72
+ },
73
+ ref
74
+ ) => {
75
+ const { alt, setStatus, status } = useRootContext();
76
+
77
+ const onLoad = React.useCallback(
78
+ (e: NativeSyntheticEvent<ImageLoadEventData>) => {
79
+ setStatus('loaded');
80
+ onLoadingStatusChange?.('loaded');
81
+ onLoadProps?.(e);
82
+ },
83
+ [onLoadProps]
84
+ );
85
+
86
+ const onError = React.useCallback(
87
+ (e: NativeSyntheticEvent<ImageErrorEventData>) => {
88
+ setStatus('error');
89
+ onLoadingStatusChange?.('error');
90
+ onErrorProps?.(e);
91
+ },
92
+ [onErrorProps]
93
+ );
94
+
95
+ if (status === 'error') {
96
+ return null;
97
+ }
98
+
99
+ return (
100
+ <RNImage
101
+ ref={ref}
102
+ alt={alt}
103
+ onLoad={onLoad}
104
+ onError={onError}
105
+ className={cn('aspect-square h-full w-full', className)}
106
+ {...props}
107
+ />
108
+ );
109
+ }
110
+ );
111
+
112
+ AvatarImage.displayName = 'AvatarImage';
113
+
114
+ const AvatarFallback = React.forwardRef<
115
+ React.ElementRef<typeof View>,
116
+ React.ComponentPropsWithoutRef<typeof View>
117
+ >(({ className, ...props }, ref) => {
118
+ const { alt, status } = useRootContext();
119
+
120
+ if (status !== 'error') {
121
+ return null;
122
+ }
123
+ return (
124
+ <View
125
+ ref={ref}
126
+ role={'img'}
127
+ aria-label={alt}
128
+ className={cn(
129
+ 'flex h-full w-full items-center justify-center rounded-full bg-muted',
130
+ className
131
+ )}
132
+ {...props}
133
+ />
134
+ );
135
+ });
136
+
137
+ AvatarFallback.displayName = 'AvatarFallback';
138
+
139
+ export { Avatar, AvatarFallback, AvatarImage };
@@ -0,0 +1,66 @@
1
+ import DateTimePicker, {
2
+ DateTimePickerAndroid,
3
+ } from '@react-native-community/datetimepicker';
4
+ import * as React from 'react';
5
+ import { Pressable, View } from 'react-native';
6
+
7
+ import { Text } from '~/components/nativewind-ui/Text';
8
+
9
+ export function DatePicker(
10
+ props: React.ComponentProps<typeof DateTimePicker> & {
11
+ mode: 'date' | 'time' | 'datetime';
12
+ }
13
+ ) {
14
+ const show = (currentMode: 'time' | 'date') => () => {
15
+ DateTimePickerAndroid.open({
16
+ value: props.value,
17
+ onChange: props.onChange,
18
+ mode: currentMode,
19
+ minimumDate: props.minimumDate,
20
+ maximumDate: props.maximumDate,
21
+ });
22
+ };
23
+
24
+ return (
25
+ <View className='flex-row gap-2.5'>
26
+ {props.mode.includes('date') && (
27
+ <View className='relative pt-1.5'>
28
+ <Pressable
29
+ onPress={show('date')}
30
+ className='border border-border active:opacity-80 px-5 py-3 rounded-md'
31
+ >
32
+ <Text variant='subhead'>
33
+ {new Intl.DateTimeFormat('en-US', {
34
+ dateStyle: 'medium',
35
+ }).format(props.value)}
36
+ </Text>
37
+ </Pressable>
38
+ <View className='absolute top-0 left-2 bg-card px-1'>
39
+ <Text variant='caption2' className='text-[10px] opacity-60'>
40
+ Date
41
+ </Text>
42
+ </View>
43
+ </View>
44
+ )}
45
+ {props.mode.includes('time') && (
46
+ <View className='relative pt-1.5'>
47
+ <Pressable
48
+ onPress={show('time')}
49
+ className='border border-border active:opacity-80 px-5 py-3 rounded-md'
50
+ >
51
+ <Text variant='subhead'>
52
+ {new Intl.DateTimeFormat('en-US', {
53
+ timeStyle: 'short',
54
+ }).format(props.value)}
55
+ </Text>
56
+ </Pressable>
57
+ <View className='absolute top-0 left-2 bg-card px-1'>
58
+ <Text variant='caption2' className='text-[10px] opacity-60'>
59
+ Time
60
+ </Text>
61
+ </View>
62
+ </View>
63
+ )}
64
+ </View>
65
+ );
66
+ }
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import DateTimePicker from '@react-native-community/datetimepicker';
3
+
4
+ export function DatePicker(
5
+ props: React.ComponentProps<typeof DateTimePicker> & {
6
+ mode: 'date' | 'time' | 'datetime';
7
+ }
8
+ ) {
9
+ return <DateTimePicker {...props} />;
10
+ }
@@ -0,0 +1,39 @@
1
+ import { Picker as RNPicker } from '@react-native-picker/picker';
2
+ import { View } from 'react-native';
3
+
4
+ import { useColorScheme } from '~/lib/useColorScheme';
5
+ import { cn } from '~/lib/cn';
6
+
7
+ export function Picker<T>({
8
+ mode = 'dropdown',
9
+ style,
10
+ dropdownIconColor,
11
+ dropdownIconRippleColor,
12
+ className,
13
+ ...props
14
+ }: React.ComponentPropsWithoutRef<typeof RNPicker<T>>) {
15
+ const { colors } = useColorScheme();
16
+ return (
17
+ <View
18
+ className={cn(
19
+ 'bg-background border border-background ios:shadow-sm ios:shadow-black/5 rounded-md',
20
+ className
21
+ )}
22
+ >
23
+ <RNPicker
24
+ mode={mode}
25
+ style={
26
+ style ?? {
27
+ backgroundColor: colors.root,
28
+ borderRadius: 8,
29
+ }
30
+ }
31
+ dropdownIconColor={dropdownIconColor ?? colors.foreground}
32
+ dropdownIconRippleColor={dropdownIconRippleColor ?? colors.foreground}
33
+ {...props}
34
+ />
35
+ </View>
36
+ );
37
+ }
38
+
39
+ export const PickerItem = RNPicker.Item;
@@ -0,0 +1,95 @@
1
+ import * as React from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ Extrapolation,
5
+ interpolate,
6
+ useAnimatedStyle,
7
+ useDerivedValue,
8
+ withSpring,
9
+ } from 'react-native-reanimated';
10
+
11
+ import { cn } from '~/lib/cn';
12
+
13
+ const DEFAULT_MAX = 100;
14
+
15
+ const ProgressIndicator = React.forwardRef<
16
+ React.ElementRef<typeof View>,
17
+ React.ComponentPropsWithoutRef<typeof View> & {
18
+ value?: number;
19
+ max?: number;
20
+ getValueLabel?: (value: number, max: number) => string;
21
+ }
22
+ >(
23
+ (
24
+ {
25
+ value: valueProp,
26
+ max: maxProp,
27
+ getValueLabel = defaultGetValueLabel,
28
+ className,
29
+ children,
30
+ ...props
31
+ },
32
+ ref
33
+ ) => {
34
+ const max = maxProp ?? DEFAULT_MAX;
35
+ const value = isValidValueNumber(valueProp, max) ? valueProp : 0;
36
+ const progress = useDerivedValue(() => value ?? 0);
37
+
38
+ const indicator = useAnimatedStyle(() => {
39
+ return {
40
+ width: withSpring(
41
+ `${interpolate(
42
+ progress.value,
43
+ [0, 100],
44
+ [1, 100],
45
+ Extrapolation.CLAMP
46
+ )}%`,
47
+ { overshootClamping: true }
48
+ ),
49
+ };
50
+ });
51
+
52
+ return (
53
+ <View
54
+ role='progressbar'
55
+ ref={ref}
56
+ aria-valuemax={max}
57
+ aria-valuemin={0}
58
+ aria-valuenow={value}
59
+ aria-valuetext={getValueLabel(value, max)}
60
+ accessibilityValue={{
61
+ min: 0,
62
+ max,
63
+ now: value,
64
+ text: getValueLabel(value, max),
65
+ }}
66
+ className={cn(
67
+ 'relative h-1 w-full overflow-hidden rounded-full',
68
+ className
69
+ )}
70
+ {...props}
71
+ >
72
+ <View className='absolute left-0 top-0 right-0 bottom-0 bg-muted opacity-20' />
73
+ <Animated.View
74
+ role='presentation'
75
+ style={indicator}
76
+ className={cn('h-full bg-primary')}
77
+ />
78
+ </View>
79
+ );
80
+ }
81
+ );
82
+
83
+ ProgressIndicator.displayName = 'ProgressIndicator';
84
+
85
+ export { ProgressIndicator };
86
+
87
+ function defaultGetValueLabel(value: number, max: number) {
88
+ return `${Math.round((value / max) * 100)}%`;
89
+ }
90
+
91
+ function isValidValueNumber(value: any, max: number): value is number {
92
+ return (
93
+ typeof value === 'number' && !isNaN(value) && value <= max && value >= 0
94
+ );
95
+ }
@@ -0,0 +1,22 @@
1
+ import RNSegmentedControl from '@react-native-segmented-control/segmented-control';
2
+ import { Platform } from 'react-native';
3
+
4
+ import { useColorScheme } from '~/lib/useColorScheme';
5
+
6
+ export function SegmentedControl(
7
+ props: React.ComponentPropsWithoutRef<typeof RNSegmentedControl>
8
+ ) {
9
+ const { colorScheme, colors } = useColorScheme();
10
+ return (
11
+ <RNSegmentedControl
12
+ backgroundColor={
13
+ Platform.OS === 'android'
14
+ ? colorScheme === 'dark'
15
+ ? colors.background
16
+ : colors.grey6
17
+ : undefined
18
+ }
19
+ {...props}
20
+ />
21
+ );
22
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ BottomSheetBackdrop,
3
+ BottomSheetBackdropProps,
4
+ BottomSheetModal,
5
+ } from '@gorhom/bottom-sheet';
6
+ import * as React from 'react';
7
+
8
+ import { useColorScheme } from '~/lib/useColorScheme';
9
+
10
+ const Sheet = React.forwardRef<
11
+ BottomSheetModal,
12
+ React.ComponentPropsWithoutRef<typeof BottomSheetModal>
13
+ >(
14
+ (
15
+ { index = 0, backgroundStyle, style, handleIndicatorStyle, ...props },
16
+ ref
17
+ ) => {
18
+ const { colors } = useColorScheme();
19
+
20
+ const renderBackdrop = React.useCallback(
21
+ (props: BottomSheetBackdropProps) => (
22
+ <BottomSheetBackdrop {...props} disappearsOnIndex={-1} />
23
+ ),
24
+ []
25
+ );
26
+ return (
27
+ <BottomSheetModal
28
+ ref={ref}
29
+ index={0}
30
+ backgroundStyle={
31
+ backgroundStyle ?? {
32
+ backgroundColor: colors.card,
33
+ }
34
+ }
35
+ style={
36
+ style ?? {
37
+ borderWidth: 1,
38
+ borderColor: colors.grey5,
39
+ borderTopStartRadius: 16,
40
+ borderTopEndRadius: 16,
41
+ }
42
+ }
43
+ handleIndicatorStyle={
44
+ handleIndicatorStyle ?? {
45
+ backgroundColor: colors.grey4,
46
+ }
47
+ }
48
+ backdropComponent={renderBackdrop}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+ );
54
+
55
+ function useSheetRef() {
56
+ return React.useRef<BottomSheetModal>(null);
57
+ }
58
+
59
+ export { Sheet, useSheetRef };
@@ -0,0 +1,28 @@
1
+ import RNSlider from '@react-native-community/slider';
2
+ import { Platform } from 'react-native';
3
+
4
+ import { useColorScheme } from '~/lib/useColorScheme';
5
+ import { COLORS } from '~/theme/colors';
6
+
7
+ export function Slider({
8
+ thumbTintColor,
9
+ minimumTrackTintColor,
10
+ maximumTrackTintColor,
11
+ ...props
12
+ }: React.ComponentPropsWithoutRef<typeof RNSlider>) {
13
+ const { colors } = useColorScheme();
14
+ return (
15
+ <RNSlider
16
+ thumbTintColor={
17
+ thumbTintColor ?? Platform.OS === 'ios' ? COLORS.white : colors.primary
18
+ }
19
+ minimumTrackTintColor={minimumTrackTintColor ?? colors.primary}
20
+ maximumTrackTintColor={
21
+ maximumTrackTintColor ?? Platform.OS === 'android'
22
+ ? colors.primary
23
+ : undefined
24
+ }
25
+ {...props}
26
+ />
27
+ );
28
+ }
@@ -0,0 +1,79 @@
1
+ import { VariantProps, cva } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+ <% if (props.stylingPackage?.options.selectedComponents.includes('selectable-text')) { %>
4
+ import { UITextView } from 'react-native-uitextview';
5
+ import { cssInterop } from 'nativewind';
6
+ <% } else { %>
7
+ import { Text as RNText } from 'react-native';
8
+ <% } %>
9
+
10
+ import { cn } from '~/lib/cn';
11
+
12
+ <% if (props.stylingPackage?.options.selectedComponents.includes('selectable-text')) { %>
13
+ cssInterop(UITextView, { className: 'style' });
14
+ <% } %>
15
+
16
+ const textVariants = cva('text-foreground', {
17
+ variants: {
18
+ variant: {
19
+ largeTitle: 'text-4xl',
20
+ title1: 'text-2xl',
21
+ title2: 'text-[22px] leading-7',
22
+ title3: 'text-xl',
23
+ heading: 'text-[17px] leading-6 font-semibold',
24
+ body: 'text-[17px] leading-6',
25
+ callout: 'text-base',
26
+ subhead: 'text-[15px] leading-6',
27
+ footnote: 'text-[13px] leading-5',
28
+ caption1: 'text-xs',
29
+ caption2: 'text-[11px] leading-4',
30
+ },
31
+ color: {
32
+ primary: '',
33
+ secondary: 'text-secondary-foreground/90',
34
+ tertiary: 'text-muted-foreground/90',
35
+ quarternary: 'text-muted-foreground/50',
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ variant: 'body',
40
+ color: 'primary',
41
+ },
42
+ });
43
+
44
+ const TextClassContext = React.createContext<string | undefined>(undefined);
45
+
46
+ <% if (props.stylingPackage?.options.selectedComponents.includes('selectable-text')) { %>
47
+ function Text({
48
+ className,
49
+ variant,
50
+ color,
51
+ ...props
52
+ }: React.ComponentPropsWithoutRef<typeof UITextView> & VariantProps<typeof textVariants>) {
53
+ const textClassName = React.useContext(TextClassContext);
54
+ return (
55
+ <UITextView
56
+ className={cn(textVariants({ variant, color }), textClassName, className)}
57
+ {...props}
58
+ />
59
+ );
60
+ }
61
+ <% } else { %>
62
+ function Text({
63
+ className,
64
+ variant,
65
+ color,
66
+ ...props
67
+ }: React.ComponentPropsWithoutRef<typeof RNText> & VariantProps<typeof textVariants>) {
68
+ const textClassName = React.useContext(TextClassContext);
69
+ return (
70
+ <RNText
71
+ className={cn(textVariants({ variant, color }), textClassName, className)}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+ <% } %>
77
+
78
+
79
+ export { Text, TextClassContext, textVariants };
@@ -0,0 +1,37 @@
1
+ import { Icon } from '@roninoss/icons';
2
+ import { Pressable, View } from 'react-native';
3
+ import Animated, { LayoutAnimationConfig, ZoomInRotate } from 'react-native-reanimated';
4
+
5
+ import { cn } from '~/lib/cn';
6
+ import { useColorScheme } from '~/lib/useColorScheme';
7
+ import { COLORS } from '~/theme/colors';
8
+
9
+ export function ThemeToggle() {
10
+ const { colorScheme, setColorScheme } = useColorScheme();
11
+ return (
12
+ <LayoutAnimationConfig skipEntering>
13
+ <Animated.View
14
+ className="items-center justify-center"
15
+ key={`toggle-${colorScheme}`}
16
+ entering={ZoomInRotate}>
17
+ <Pressable
18
+ onPress={() => {
19
+ setColorScheme(colorScheme === 'dark' ? 'light' : 'dark');
20
+ }}
21
+ className="opacity-80">
22
+ {colorScheme === 'dark'
23
+ ? ({ pressed }) => (
24
+ <View className={cn('px-0.5', pressed && 'opacity-50')}>
25
+ <Icon namingScheme="sfSymbol" name="moon.stars" color={COLORS.white} />
26
+ </View>
27
+ )
28
+ : ({ pressed }) => (
29
+ <View className={cn('px-0.5', pressed && 'opacity-50')}>
30
+ <Icon namingScheme="sfSymbol" name="sun.min" color={COLORS.black} />
31
+ </View>
32
+ )}
33
+ </Pressable>
34
+ </Animated.View>
35
+ </LayoutAnimationConfig>
36
+ );
37
+ }
@@ -0,0 +1,17 @@
1
+ import { useColorScheme } from '~/lib/useColorScheme';
2
+ import { COLORS } from '~/theme/colors';
3
+ import { Switch } from 'react-native';
4
+
5
+ export function Toggle(props: React.ComponentPropsWithoutRef<typeof Switch>) {
6
+ const { colors } = useColorScheme();
7
+ return (
8
+ <Switch
9
+ trackColor={{
10
+ true: colors.primary,
11
+ false: colors.grey,
12
+ }}
13
+ thumbColor={COLORS.white}
14
+ {...props}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,3 @@
1
+ /// <reference types="expo/types" />
2
+
3
+ // NOTE: This file should not be edited and should be in your git ignore