refactor: reorganize examples with shared package (#306)
* feat(expo-example): update to match example app structure - Add promise-based present/dismiss to TrueSheet.web.tsx - Remove tabs navigation, use stack-based routing - Add screens: Map, Standard, Modal, Test - Add shared components and sheet components - Add utils (constants, times, random) - Add react-native-maps dependency * feat(expo-example): add Map component and SheetStack with withLayoutContext - Add platform-specific Map component (native MapView, web View fallback) - Add sheet-stack route using expo-router's withLayoutContext - Integrate createTrueSheetNavigator with expo-router file-based routing * refactor: create shared example-shared package for common components - Create @truesheet/example-shared workspace package - Move common components (Button, Header, Footer, etc.) to shared - Move sheet components (BasicSheet, PromptSheet, etc.) to shared - Move utils (constants, times, random) to shared - Update example and expo-example to re-export from shared * chore: reorganize examples into examples/ folder - Move example/ to examples/bare/ - Move expo-example/ to examples/expo/ - Move example-shared/ to examples/shared/ - Update workspace paths in root package.json - Rename packages to @truesheet/bare-example, @truesheet/expo-example - Update script names (example -> bare, expo) * chore: update config paths for examples folder reorganization * chore: rename examples to example and update package names to @example/* * chore: move screen components to shared package - Add MapScreen, ModalScreen, StandardScreen, TestScreen to shared - Make screens navigation-agnostic with callback props - Add MapComponent prop to MapScreen for platform-specific map - Create Map component in bare example - Update expo and bare examples to use shared screens * chore: import screens directly from @example/shared/screens * chore: import components and utils directly from @example/shared * chore: remove unused sheets index files * chore: move Map component to shared package * chore: remove unused constants folder from expo example * chore: remove unused ReanimatedExample component * chore: update scripts for new example folder structure * chore: exclude example folder from jest test paths * chore: add expo prebuild step to clean script * fix: update config paths for new example folder structure * chore: categorize steps in clean script * fix: use workspace:* for example dependencies - Change @lodev09/react-native-true-sheet from * to workspace:* in bare and expo examples - Fixes duplicate view registration error caused by npm version being installed alongside workspace - Silence clean.sh script output while preserving error visibility
4
.gitignore
vendored
@ -43,10 +43,10 @@ android.iml
|
||||
|
||||
# Cocoapods
|
||||
#
|
||||
example/ios/Pods
|
||||
example/bare/ios/Pods
|
||||
|
||||
# Ruby
|
||||
example/vendor/
|
||||
example/bare/vendor/
|
||||
|
||||
# node.js
|
||||
#
|
||||
|
||||
20
AGENTS.md
@ -22,7 +22,9 @@ src/
|
||||
├── specs/ # TurboModule spec
|
||||
├── reanimated/ # Reanimated integration
|
||||
├── navigation/ # React Navigation integration
|
||||
│ └── screen/ # Screen components for navigator
|
||||
├── TrueSheet.tsx # Main React component
|
||||
├── TrueSheetProvider.tsx
|
||||
└── TrueSheet.types.ts
|
||||
|
||||
ios/
|
||||
@ -30,9 +32,10 @@ ios/
|
||||
├── TrueSheetViewController.mm # UIViewController for sheet presentation
|
||||
├── TrueSheetModule.mm # TurboModule
|
||||
├── TrueSheet*View.mm # Container, Content, Header, Footer views
|
||||
├── core/ # Core UI components (GrabberView, BlurView)
|
||||
├── events/ # Event classes
|
||||
└── utils/ # Utility classes
|
||||
├── TrueSheetComponentDescriptor.h
|
||||
├── core/ # GrabberView, BlurView
|
||||
├── events/ # Lifecycle, State, Drag, Focus events
|
||||
└── utils/ # Layout, Gesture, Window utilities
|
||||
|
||||
android/.../truesheet/
|
||||
├── TrueSheetView.kt # Host view
|
||||
@ -40,13 +43,14 @@ android/.../truesheet/
|
||||
├── TrueSheetModule.kt # TurboModule
|
||||
├── TrueSheet*View.kt # Container, Content, Header, Footer views
|
||||
├── TrueSheet*ViewManager.kt # View managers
|
||||
├── core/ # Core components (GrabberView, DialogObserver, etc.)
|
||||
├── events/ # Event classes
|
||||
└── utils/ # Utility classes
|
||||
├── TrueSheetPackage.kt
|
||||
├── core/ # GrabberView, DialogObserver, RNScreensFragmentObserver
|
||||
├── events/ # Lifecycle, State, Drag, Focus events
|
||||
└── utils/ # ScreenUtils
|
||||
|
||||
common/cpp/.../TrueSheetSpec/
|
||||
├── TrueSheetViewState.h/.cpp # Shared state
|
||||
├── TrueSheetViewShadowNode.h/.cpp # Custom shadow node
|
||||
├── TrueSheetViewState.cpp/.h
|
||||
├── TrueSheetViewShadowNode.cpp/.h
|
||||
└── TrueSheetViewComponentDescriptor.h
|
||||
```
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,8 +1,8 @@
|
||||
const path = require('path')
|
||||
const { getConfig } = require('react-native-builder-bob/babel-config')
|
||||
const pkg = require('../package.json')
|
||||
const pkg = require('../../package.json')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const root = path.resolve(__dirname, '../..')
|
||||
|
||||
module.exports = getConfig(
|
||||
{
|
||||
@ -2845,7 +2845,7 @@ DEPENDENCIES:
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
- RNTrueSheet (from `../..`)
|
||||
- RNTrueSheet (from `../../..`)
|
||||
- RNWorklets (from `../node_modules/react-native-worklets`)
|
||||
- SocketRocket (~> 0.7.1)
|
||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
@ -3011,7 +3011,7 @@ EXTERNAL SOURCES:
|
||||
RNScreens:
|
||||
:path: "../node_modules/react-native-screens"
|
||||
RNTrueSheet:
|
||||
:path: "../.."
|
||||
:path: "../../.."
|
||||
RNWorklets:
|
||||
:path: "../node_modules/react-native-worklets"
|
||||
Yoga:
|
||||
@ -2,8 +2,8 @@ const path = require('path')
|
||||
const { getDefaultConfig } = require('@react-native/metro-config')
|
||||
const { withMetroConfig } = require('react-native-monorepo-config')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const pkg = require('../package.json')
|
||||
const root = path.resolve(__dirname, '../..')
|
||||
const pkg = require('../../package.json')
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "react-native-true-sheet-example",
|
||||
"name": "@example/bare",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@ -11,6 +11,8 @@
|
||||
"doctor": "react-native doctor"
|
||||
},
|
||||
"dependencies": {
|
||||
"@example/shared": "*",
|
||||
"@lodev09/react-native-true-sheet": "workspace:*",
|
||||
"@react-navigation/native": "^7.1.21",
|
||||
"@react-navigation/native-stack": "^7.7.0",
|
||||
"react": "19.1.1",
|
||||
@ -1,5 +1,5 @@
|
||||
const path = require('path');
|
||||
const pkg = require('../package.json');
|
||||
const pkg = require('../../package.json');
|
||||
|
||||
module.exports = {
|
||||
project: {
|
||||
@ -9,7 +9,7 @@ module.exports = {
|
||||
},
|
||||
dependencies: {
|
||||
[pkg.name]: {
|
||||
root: path.join(__dirname, '..'),
|
||||
root: path.join(__dirname, '../..'),
|
||||
platforms: {
|
||||
ios: {},
|
||||
},
|
||||
27
example/bare/src/navigators/ModalStackNavigator.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { ModalScreen, TestScreen } from '@example/shared/screens';
|
||||
import type { ModalStackParamList } from '../types';
|
||||
|
||||
const ModalStack = createNativeStackNavigator<ModalStackParamList>();
|
||||
|
||||
const ModalScreenWrapper = () => {
|
||||
const navigation = useNavigation<NativeStackNavigationProp<ModalStackParamList>>();
|
||||
return (
|
||||
<ModalScreen
|
||||
onNavigateToTest={() => navigation.navigate('Test')}
|
||||
onDismiss={() => navigation.goBack()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ModalStackNavigator = () => {
|
||||
return (
|
||||
<ModalStack.Navigator screenOptions={{ headerTransparent: true, headerTintColor: 'white' }}>
|
||||
<ModalStack.Screen name="Modal" component={ModalScreenWrapper} />
|
||||
<ModalStack.Screen name="Test" component={TestScreen} />
|
||||
</ModalStack.Navigator>
|
||||
);
|
||||
};
|
||||
@ -1,14 +1,38 @@
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
|
||||
import { MapScreen, StandardScreen, TestScreen } from '../screens';
|
||||
import { MapScreen, StandardScreen, TestScreen } from '@example/shared/screens';
|
||||
import { Map } from '@example/shared/components';
|
||||
import { ModalStackNavigator } from './ModalStackNavigator';
|
||||
import { SheetNavigator } from './SheetNavigator';
|
||||
import type { AppStackParamList } from '../types';
|
||||
import { useAppNavigation } from '../hooks';
|
||||
|
||||
const Stack = createNativeStackNavigator<AppStackParamList>();
|
||||
|
||||
const INITIAL_ROUTE_NAME: keyof AppStackParamList = 'Map';
|
||||
|
||||
const MapScreenWrapper = () => {
|
||||
const navigation = useAppNavigation();
|
||||
return (
|
||||
<MapScreen
|
||||
MapComponent={Map}
|
||||
onNavigateToModal={() => navigation.navigate('ModalStack')}
|
||||
onNavigateToSheetStack={() => navigation.navigate('SheetStack')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StandardScreenWrapper = () => {
|
||||
const navigation = useAppNavigation();
|
||||
return (
|
||||
<StandardScreen
|
||||
onNavigateToTest={() => navigation.navigate('Test')}
|
||||
onNavigateToModal={() => navigation.navigate('ModalStack')}
|
||||
onNavigateToMap={() => navigation.navigate('Map')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const RootNavigator = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
@ -20,11 +44,11 @@ export const RootNavigator = () => {
|
||||
name="SheetStack"
|
||||
component={SheetNavigator}
|
||||
/>
|
||||
<Stack.Screen options={{ headerShown: false }} name="Map" component={MapScreen} />
|
||||
<Stack.Screen options={{ headerShown: false }} name="Map" component={MapScreenWrapper} />
|
||||
<Stack.Screen
|
||||
options={{ headerShown: false, title: 'Standard' }}
|
||||
name="Standard"
|
||||
component={StandardScreen}
|
||||
component={StandardScreenWrapper}
|
||||
/>
|
||||
<Stack.Screen name="Test" component={TestScreen} />
|
||||
<Stack.Screen
|
||||
@ -6,8 +6,8 @@ import {
|
||||
createTrueSheetNavigator,
|
||||
useTrueSheetNavigation,
|
||||
} from '@lodev09/react-native-true-sheet/navigation';
|
||||
import { Button, DemoContent } from '../components';
|
||||
import { BLUE, DARK, GAP, LIGHT_GRAY, SPACING } from '../utils';
|
||||
import { Button, DemoContent } from '@example/shared/components';
|
||||
import { BLUE, DARK, GAP, LIGHT_GRAY, SPACING } from '@example/shared/utils';
|
||||
import type { AppStackParamList, SheetStackParamList } from '../types';
|
||||
|
||||
const Sheet = createTrueSheetNavigator<SheetStackParamList>();
|
||||
@ -4,19 +4,17 @@ import { useFonts } from 'expo-font';
|
||||
import { Stack } from 'expo-router';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
import { useEffect } from 'react';
|
||||
import { TrueSheetProvider } from '@lodev09/react-native-true-sheet';
|
||||
import { useColorScheme } from 'react-native';
|
||||
import { ReanimatedTrueSheetProvider } from '@lodev09/react-native-true-sheet/reanimated';
|
||||
import 'react-native-reanimated';
|
||||
|
||||
import { useColorScheme } from '../components/useColorScheme';
|
||||
|
||||
export {
|
||||
// Catch any errors thrown by the Layout component.
|
||||
ErrorBoundary,
|
||||
} from 'expo-router';
|
||||
|
||||
export const unstable_settings = {
|
||||
// Ensure that reloading on `/modal` keeps a back button present.
|
||||
initialRouteName: '(tabs)',
|
||||
initialRouteName: 'index',
|
||||
};
|
||||
|
||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||
@ -51,12 +49,21 @@ function RootLayoutNav() {
|
||||
|
||||
return (
|
||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<TrueSheetProvider>
|
||||
<Stack>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
|
||||
<ReanimatedTrueSheetProvider>
|
||||
<Stack screenOptions={{ headerTransparent: true, headerTintColor: 'white' }}>
|
||||
<Stack.Screen name="index" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="standard" options={{ headerShown: false, title: 'Standard' }} />
|
||||
<Stack.Screen name="test" options={{ title: 'Test' }} />
|
||||
<Stack.Screen
|
||||
name="modal"
|
||||
options={{ presentation: 'fullScreenModal', headerShown: false }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="sheet-stack"
|
||||
options={{ presentation: 'fullScreenModal', headerShown: false }}
|
||||
/>
|
||||
</Stack>
|
||||
</TrueSheetProvider>
|
||||
</ReanimatedTrueSheetProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
16
example/expo/app/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { useRouter } from 'expo-router';
|
||||
|
||||
import { MapScreen } from '@example/shared/screens';
|
||||
import { Map } from '@example/shared/components';
|
||||
|
||||
export default function Index() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<MapScreen
|
||||
MapComponent={Map}
|
||||
onNavigateToModal={() => router.push('/modal')}
|
||||
onNavigateToSheetStack={() => router.push('/sheet-stack')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
11
example/expo/app/modal.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { useRouter } from 'expo-router';
|
||||
|
||||
import { ModalScreen } from '@example/shared/screens';
|
||||
|
||||
export default function Modal() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<ModalScreen onNavigateToTest={() => router.push('/test')} onDismiss={() => router.back()} />
|
||||
);
|
||||
}
|
||||
70
example/expo/app/sheet-stack/_layout.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { withLayoutContext } from 'expo-router';
|
||||
import { createTrueSheetNavigator } from '@lodev09/react-native-true-sheet/navigation';
|
||||
|
||||
import { DARK } from '@example/shared/utils';
|
||||
|
||||
const { Navigator } = createTrueSheetNavigator();
|
||||
|
||||
export const Sheet = withLayoutContext(Navigator);
|
||||
|
||||
export default function SheetStackLayout() {
|
||||
return (
|
||||
<Sheet
|
||||
screenListeners={{
|
||||
sheetWillPresent: (e) => {
|
||||
console.log(`[SheetNavigator] sheetWillPresent: index=${e.data.index}`);
|
||||
},
|
||||
sheetDidPresent: (e) => {
|
||||
console.log(`[SheetNavigator] sheetDidPresent: index=${e.data.index}`);
|
||||
},
|
||||
sheetWillDismiss: () => {
|
||||
console.log('[SheetNavigator] sheetWillDismiss');
|
||||
},
|
||||
sheetDidDismiss: () => {
|
||||
console.log('[SheetNavigator] sheetDidDismiss');
|
||||
},
|
||||
sheetDetentChange: (e) => {
|
||||
console.log(`[SheetNavigator] sheetDetentChange: index=${e.data.index}`);
|
||||
},
|
||||
sheetDragBegin: (e) => {
|
||||
console.log(`[SheetNavigator] sheetDragBegin: index=${e.data.index}`);
|
||||
},
|
||||
sheetDragChange: (e) => {
|
||||
console.log(`[SheetNavigator] sheetDragChange: position=${e.data.position.toFixed(0)}`);
|
||||
},
|
||||
sheetDragEnd: (e) => {
|
||||
console.log(`[SheetNavigator] sheetDragEnd: index=${e.data.index}`);
|
||||
},
|
||||
sheetWillFocus: () => {
|
||||
console.log('[SheetNavigator] sheetWillFocus');
|
||||
},
|
||||
sheetDidFocus: () => {
|
||||
console.log('[SheetNavigator] sheetDidFocus');
|
||||
},
|
||||
sheetWillBlur: () => {
|
||||
console.log('[SheetNavigator] sheetWillBlur');
|
||||
},
|
||||
sheetDidBlur: () => {
|
||||
console.log('[SheetNavigator] sheetDidBlur');
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Sheet.Screen name="index" />
|
||||
<Sheet.Screen
|
||||
name="details"
|
||||
options={{
|
||||
detents: ['auto', 1],
|
||||
cornerRadius: 16,
|
||||
}}
|
||||
/>
|
||||
<Sheet.Screen
|
||||
name="settings"
|
||||
options={{
|
||||
detents: ['auto', 1],
|
||||
backgroundColor: DARK,
|
||||
cornerRadius: 16,
|
||||
}}
|
||||
/>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
43
example/expo/app/sheet-stack/details.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { useTrueSheetNavigation } from '@lodev09/react-native-true-sheet/navigation';
|
||||
|
||||
import { Button, DemoContent } from '@example/shared/components';
|
||||
import { GAP, LIGHT_GRAY, SPACING } from '@example/shared/utils';
|
||||
|
||||
export default function DetailsSheet() {
|
||||
const navigation = useTrueSheetNavigation();
|
||||
|
||||
return (
|
||||
<View style={styles.sheetContent}>
|
||||
<Text style={styles.sheetTitle}>Details Sheet</Text>
|
||||
<Text style={styles.sheetSubtitle}>This is a sheet screen using expo-router.</Text>
|
||||
<DemoContent />
|
||||
<View style={styles.buttons}>
|
||||
<Button text="Resize to 100%" onPress={() => navigation.resize(1)} />
|
||||
<Button text="Open Settings" onPress={() => navigation.navigate('settings')} />
|
||||
<Button text="Go Back" onPress={() => navigation.goBack()} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
sheetContent: {
|
||||
padding: SPACING,
|
||||
gap: GAP,
|
||||
},
|
||||
sheetTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
color: 'white',
|
||||
},
|
||||
sheetSubtitle: {
|
||||
fontSize: 14,
|
||||
color: LIGHT_GRAY,
|
||||
marginBottom: SPACING,
|
||||
},
|
||||
buttons: {
|
||||
gap: GAP,
|
||||
marginTop: SPACING,
|
||||
},
|
||||
});
|
||||
49
example/expo/app/sheet-stack/index.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useTrueSheetNavigation } from '@lodev09/react-native-true-sheet/navigation';
|
||||
|
||||
import { Button } from '@example/shared/components';
|
||||
import { BLUE, GAP, LIGHT_GRAY, SPACING } from '@example/shared/utils';
|
||||
|
||||
export default function SheetHomeScreen() {
|
||||
const navigation = useTrueSheetNavigation();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<View style={styles.heading}>
|
||||
<Text style={styles.title}>Sheet Navigator</Text>
|
||||
<Text style={styles.subtitle}>
|
||||
Using createTrueSheetNavigator with expo-router's withLayoutContext.
|
||||
</Text>
|
||||
</View>
|
||||
<Button text="Open Details Sheet" onPress={() => navigation.navigate('details')} />
|
||||
<Button text="Open Settings Sheet" onPress={() => navigation.navigate('settings')} />
|
||||
<Button text="Navigate to Test" onPress={() => router.push('/test')} />
|
||||
<Button text="Go Back" onPress={() => router.back()} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
backgroundColor: BLUE,
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
padding: SPACING,
|
||||
gap: GAP,
|
||||
},
|
||||
heading: {
|
||||
marginBottom: SPACING * 2,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
lineHeight: 30,
|
||||
fontWeight: '500',
|
||||
color: 'white',
|
||||
},
|
||||
subtitle: {
|
||||
lineHeight: 24,
|
||||
color: LIGHT_GRAY,
|
||||
},
|
||||
});
|
||||
42
example/expo/app/sheet-stack/settings.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { useTrueSheetNavigation } from '@lodev09/react-native-true-sheet/navigation';
|
||||
|
||||
import { Button, DemoContent } from '@example/shared/components';
|
||||
import { GAP, LIGHT_GRAY, SPACING } from '@example/shared/utils';
|
||||
|
||||
export default function SettingsSheet() {
|
||||
const navigation = useTrueSheetNavigation();
|
||||
|
||||
return (
|
||||
<View style={styles.sheetContent}>
|
||||
<Text style={styles.sheetTitle}>Settings Sheet</Text>
|
||||
<Text style={styles.sheetSubtitle}>Another sheet in the navigation stack.</Text>
|
||||
<DemoContent />
|
||||
<View style={styles.buttons}>
|
||||
<Button text="Resize to 100%" onPress={() => navigation.resize(1)} />
|
||||
<Button text="Go Back" onPress={() => navigation.goBack()} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
sheetContent: {
|
||||
padding: SPACING,
|
||||
gap: GAP,
|
||||
},
|
||||
sheetTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
color: 'white',
|
||||
},
|
||||
sheetSubtitle: {
|
||||
fontSize: 14,
|
||||
color: LIGHT_GRAY,
|
||||
marginBottom: SPACING,
|
||||
},
|
||||
buttons: {
|
||||
gap: GAP,
|
||||
marginTop: SPACING,
|
||||
},
|
||||
});
|
||||
15
example/expo/app/standard.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { useRouter } from 'expo-router';
|
||||
|
||||
import { StandardScreen } from '@example/shared/screens';
|
||||
|
||||
export default function Standard() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<StandardScreen
|
||||
onNavigateToTest={() => router.push('/test')}
|
||||
onNavigateToModal={() => router.push('/modal')}
|
||||
onNavigateToMap={() => router.push('/')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
example/expo/app/test.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { TestScreen } from '@example/shared/screens';
|
||||
|
||||
export default function Test() {
|
||||
return <TestScreen />;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@ -2,8 +2,8 @@ const path = require('path');
|
||||
const { getDefaultConfig } = require('expo/metro-config');
|
||||
const { withMetroConfig } = require('react-native-monorepo-config');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const pkg = require('../package.json');
|
||||
const root = path.resolve(__dirname, '../..');
|
||||
const pkg = require('../../package.json');
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "expo-example",
|
||||
"name": "@example/expo",
|
||||
"main": "expo-router/entry",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
@ -11,9 +11,10 @@
|
||||
"prebuild:clean": "expo prebuild --clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@example/shared": "*",
|
||||
"@expo/vector-icons": "^15.0.3",
|
||||
"@gorhom/bottom-sheet": "^5.2.8",
|
||||
"@lodev09/react-native-true-sheet": "*",
|
||||
"@lodev09/react-native-true-sheet": "workspace:*",
|
||||
"@react-navigation/native": "^7.1.8",
|
||||
"expo": "~54.0.27",
|
||||
"expo-constants": "~18.0.11",
|
||||
@ -22,11 +23,13 @@
|
||||
"expo-router": "~6.0.17",
|
||||
"expo-splash-screen": "~31.0.12",
|
||||
"expo-status-bar": "~3.0.9",
|
||||
"expo-system-ui": "~6.0.9",
|
||||
"expo-web-browser": "~15.0.10",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-maps": "^1.26.20",
|
||||
"react-native-reanimated": "^4.2.0",
|
||||
"react-native-safe-area-context": "~5.6.0",
|
||||
"react-native-screens": "~4.16.0",
|
||||
@ -35,6 +38,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "~19.1.0",
|
||||
"@types/react-native-maps": "^0.24.2",
|
||||
"react-native-monorepo-config": "^0.1.9",
|
||||
"react-test-renderer": "19.1.0",
|
||||
"typescript": "~5.9.2"
|
||||
32
example/shared/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@example/shared",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"source": "./src/index.ts",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./components": "./src/components/index.ts",
|
||||
"./sheets": "./src/components/sheets/index.ts",
|
||||
"./screens": "./src/screens/index.ts",
|
||||
"./utils": "./src/utils/index.ts"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@lodev09/react-native-true-sheet": "*",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "*",
|
||||
"react-native-maps": "*",
|
||||
"react-native-reanimated": "*",
|
||||
"react-native-safe-area-context": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "19.1.1",
|
||||
"react-native-gesture-handler": "^2.29.1",
|
||||
"react-native-maps": "^1.26.20",
|
||||
"react-native-reanimated": "^4.2.0",
|
||||
"react-native-safe-area-context": "^5.5.2",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
@ -11,10 +11,10 @@ export const Button = (props: ButtonProps) => {
|
||||
const { text, style, loading, ...rest } = props;
|
||||
return (
|
||||
<Pressable
|
||||
style={({ pressed }) => [
|
||||
style={(state) => [
|
||||
styles.button,
|
||||
pressed && styles.pressed,
|
||||
typeof style === 'function' ? style({ pressed }) : style,
|
||||
state.pressed && styles.pressed,
|
||||
typeof style === 'function' ? style(state) : style,
|
||||
]}
|
||||
{...rest}
|
||||
>
|
||||
@ -1,8 +1,8 @@
|
||||
import { StyleSheet, Platform, type ViewProps } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import { HEADER_HEIGHT, SPACING } from '../utils';
|
||||
import { Input } from './Input';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
type HeaderProps = ViewProps;
|
||||
|
||||
33
example/shared/src/components/Map.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import MapView, { type MapViewProps } from 'react-native-maps';
|
||||
|
||||
import { BLUE } from '../utils';
|
||||
|
||||
export const Map = (props: MapViewProps) => {
|
||||
const { style, ...rest } = props;
|
||||
|
||||
return (
|
||||
<MapView
|
||||
style={[styles.map, style]}
|
||||
initialCamera={{
|
||||
altitude: 18000,
|
||||
zoom: 14,
|
||||
center: {
|
||||
latitude: 9.306743705457553,
|
||||
longitude: 123.30474002203727,
|
||||
},
|
||||
pitch: 0,
|
||||
heading: 0,
|
||||
}}
|
||||
userInterfaceStyle="dark"
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
map: {
|
||||
flex: 1,
|
||||
backgroundColor: BLUE,
|
||||
},
|
||||
});
|
||||
16
example/shared/src/components/Map.web.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { StyleSheet, View, type ViewProps } from 'react-native';
|
||||
|
||||
import { BLUE } from '../utils';
|
||||
|
||||
export const Map = (props: ViewProps) => {
|
||||
const { style, ...rest } = props;
|
||||
|
||||
return <View style={[styles.map, style]} {...rest} />;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
map: {
|
||||
flex: 1,
|
||||
backgroundColor: BLUE,
|
||||
},
|
||||
});
|
||||
@ -4,3 +4,4 @@ export * from './DemoContent';
|
||||
export * from './Button';
|
||||
export * from './Spacer';
|
||||
export * from './Input';
|
||||
export * from './Map';
|
||||
@ -7,15 +7,16 @@ import { DemoContent } from '../DemoContent';
|
||||
import { Footer } from '../Footer';
|
||||
import { Button } from '../Button';
|
||||
import { Spacer } from '../Spacer';
|
||||
import { useAppNavigation } from '../../hooks';
|
||||
|
||||
interface BasicSheetProps extends TrueSheetProps {}
|
||||
interface BasicSheetProps extends TrueSheetProps {
|
||||
onNavigateToModal?: () => void;
|
||||
}
|
||||
|
||||
export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet>) => {
|
||||
const { onNavigateToModal, ...rest } = props;
|
||||
const sheetRef = useRef<TrueSheet>(null);
|
||||
const childSheet = useRef<TrueSheet>(null);
|
||||
const [contentCount, setContentCount] = useState(0);
|
||||
const navigation = useAppNavigation();
|
||||
|
||||
const resize = async (index: number) => {
|
||||
await sheetRef.current?.resize(index);
|
||||
@ -28,16 +29,12 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
|
||||
};
|
||||
|
||||
const presentChild = async () => {
|
||||
// Note: no need to dismiss this sheet 😎
|
||||
await childSheet.current?.present();
|
||||
|
||||
console.log('Child sheet presented!');
|
||||
};
|
||||
|
||||
const presentPromptSheet = async () => {
|
||||
// Note: we need to dismiss this sheet first
|
||||
await sheetRef.current?.dismiss();
|
||||
|
||||
await TrueSheet.present('prompt-sheet');
|
||||
};
|
||||
|
||||
@ -92,11 +89,10 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
|
||||
)
|
||||
}
|
||||
onMount={() => {
|
||||
// sheetRef.current?.present(1)
|
||||
console.log('BasicSheet is ready!');
|
||||
}}
|
||||
footer={<Footer />}
|
||||
{...props}
|
||||
{...rest}
|
||||
>
|
||||
{Array.from({ length: contentCount }, (_, i) => (
|
||||
<DemoContent key={i} color={DARK_BLUE} />
|
||||
@ -110,7 +106,7 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
|
||||
<Spacer />
|
||||
<Button text="Present Child Sheet" onPress={presentChild} />
|
||||
<Button text="Present PromptSheet" onPress={presentPromptSheet} />
|
||||
<Button text="Navigate to Modal" onPress={() => navigation.navigate('ModalStack')} />
|
||||
{onNavigateToModal && <Button text="Navigate to Modal" onPress={onNavigateToModal} />}
|
||||
<Spacer />
|
||||
<Button text="Dismiss" onPress={dismiss} />
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { forwardRef, type Ref } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
import { TrueSheet, type TrueSheetProps } from '@lodev09/react-native-true-sheet';
|
||||
|
||||
@ -6,7 +6,7 @@ import { DARK, SPACING } from '../../utils';
|
||||
|
||||
interface BlankSheetProps extends TrueSheetProps {}
|
||||
|
||||
export const BlankSheet = forwardRef((props: BlankSheetProps, ref: Ref<TrueSheet>) => {
|
||||
export const BlankSheet = forwardRef<TrueSheet, BlankSheetProps>((props, ref) => {
|
||||
return (
|
||||
<TrueSheet
|
||||
ref={ref}
|
||||
@ -1,4 +1,4 @@
|
||||
import { forwardRef, type Ref } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { StyleSheet, FlatList, View } from 'react-native';
|
||||
import { TrueSheet, type TrueSheetProps } from '@lodev09/react-native-true-sheet';
|
||||
|
||||
@ -10,7 +10,7 @@ import { Header } from '../Header';
|
||||
|
||||
interface FlatListSheetProps extends TrueSheetProps {}
|
||||
|
||||
export const FlatListSheet = forwardRef((props: FlatListSheetProps, ref: Ref<TrueSheet>) => {
|
||||
export const FlatListSheet = forwardRef<TrueSheet, FlatListSheetProps>((props, ref) => {
|
||||
return (
|
||||
<TrueSheet
|
||||
ref={ref}
|
||||
@ -76,7 +76,7 @@ export const GestureSheet = forwardRef((props: GestureSheetProps, ref: Ref<TrueS
|
||||
))}
|
||||
</Animated.View>
|
||||
</GestureDetector>
|
||||
<Button text="Dismis" onPress={dismiss} />
|
||||
<Button text="Dismiss" onPress={dismiss} />
|
||||
</GestureHandlerRootView>
|
||||
</TrueSheet>
|
||||
);
|
||||
@ -5,24 +5,22 @@ import type { TrueSheetProps } from '@lodev09/react-native-true-sheet';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { GAP, SPACING } from '../../utils';
|
||||
import { useAppNavigation } from '../../hooks';
|
||||
|
||||
export const NavigationSheet = forwardRef<TrueSheet, TrueSheetProps>((props, ref) => {
|
||||
const navigation = useAppNavigation();
|
||||
interface NavigationSheetProps extends TrueSheetProps {
|
||||
onOpenModal?: () => void;
|
||||
}
|
||||
|
||||
const handleOpenModal = () => {
|
||||
navigation.navigate('ModalStack');
|
||||
};
|
||||
export const NavigationSheet = forwardRef<TrueSheet, NavigationSheetProps>((props, ref) => {
|
||||
const { onOpenModal, ...rest } = props;
|
||||
|
||||
return (
|
||||
<TrueSheet ref={ref} detents={['auto']} dimmed={false} {...props}>
|
||||
<TrueSheet ref={ref} detents={['auto']} dimmed={false} {...rest}>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Navigation Sheet</Text>
|
||||
<Text style={styles.description}>
|
||||
This sheet demonstrates opening a React Navigation fullScreenModal from within a
|
||||
TrueSheet.
|
||||
This sheet demonstrates opening a fullScreenModal from within a TrueSheet.
|
||||
</Text>
|
||||
<Button text="Open Full Screen Modal" onPress={handleOpenModal} />
|
||||
{onOpenModal && <Button text="Open Full Screen Modal" onPress={onOpenModal} />}
|
||||
</View>
|
||||
</TrueSheet>
|
||||
);
|
||||
@ -57,8 +57,6 @@ export const PromptSheet = forwardRef((props: PromptSheetProps, ref: Ref<TrueShe
|
||||
}
|
||||
onBackPress={() => {
|
||||
console.log('Back button pressed!');
|
||||
// Handle custom back press behavior here
|
||||
// For example, dismiss the sheet programmatically
|
||||
sheetRef.current?.dismiss();
|
||||
}}
|
||||
footer={<Footer />}
|
||||
@ -68,7 +66,7 @@ export const PromptSheet = forwardRef((props: PromptSheetProps, ref: Ref<TrueShe
|
||||
<Input />
|
||||
{isSubmitted && <Input />}
|
||||
<Button text="Submit" onPress={submit} />
|
||||
<Button text="Dismis" onPress={dismiss} />
|
||||
<Button text="Dismiss" onPress={dismiss} />
|
||||
</TrueSheet>
|
||||
);
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
import { forwardRef, useState, type Ref } from 'react';
|
||||
import { forwardRef, useState } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
@ -16,7 +16,6 @@ import { Header } from '../Header';
|
||||
|
||||
interface ScrollViewSheetProps extends TrueSheetProps {}
|
||||
|
||||
// Heavy item component for testing
|
||||
const HeavyItem = ({ index }: { index: number }) => {
|
||||
const [imageLoaded, setImageLoaded] = useState(false);
|
||||
|
||||
@ -40,7 +39,7 @@ const HeavyItem = ({ index }: { index: number }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ScrollViewSheet = forwardRef((props: ScrollViewSheetProps, ref: Ref<TrueSheet>) => {
|
||||
export const ScrollViewSheet = forwardRef<TrueSheet, ScrollViewSheetProps>((props, ref) => {
|
||||
return (
|
||||
<TrueSheet
|
||||
ref={ref}
|
||||
3
example/shared/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './components';
|
||||
export * from './components/sheets';
|
||||
export * from './utils';
|
||||