diff --git a/apps/bare-expo/ios/Podfile.lock b/apps/bare-expo/ios/Podfile.lock index eda94b27d4c904..0fca4da59da7ce 100644 --- a/apps/bare-expo/ios/Podfile.lock +++ b/apps/bare-expo/ios/Podfile.lock @@ -476,6 +476,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies + - RNWorklets - Yoga - ExpoModulesCore/Tests (3.0.16): - ExpoModulesJSI @@ -501,6 +502,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies + - RNWorklets - Yoga - ExpoModulesJSI (3.0.16): - hermes-engine @@ -3796,7 +3798,7 @@ SPEC CHECKSUMS: ExpoMaps: ac5024fd6b3db82da997bdc4074bda5c3e2e7764 ExpoMediaLibrary: 648cee3f5dcba13410ec9cc8ac9a426e89a61a31 ExpoMeshGradient: 763087d3b1e6e9a0974e9700ea24cb598816d93c - ExpoModulesCore: 22f2efcefda2486b06a0b9f0dcaab8eccdfaf0b4 + ExpoModulesCore: 75234558d61ae2c7b81d21526a9a37a0ec957c0f ExpoModulesJSI: c470ea2ed825fce73bdc4ef060c8a22e3f664092 ExpoModulesTestCore: e65555b75a4ed7dd3bcf421ad01d7748bd372c88 ExpoNetwork: 97073786edfe405aba5d0987a544617ed0671ad1 diff --git a/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt b/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt index acb02972facde6..8ff0b2d778ae25 100644 --- a/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt +++ b/apps/expo-go/android/expoview/src/main/java/versioned/host/exp/exponent/ExperiencePackagePicker.kt @@ -16,6 +16,7 @@ import expo.modules.clipboard.ClipboardModule import expo.modules.constants.ConstantsModule import expo.modules.constants.ConstantsService import expo.modules.contacts.ContactsModule +import expo.modules.contacts.next.ContactsNextModule import expo.modules.core.interfaces.Package import expo.modules.crypto.CryptoModule import expo.modules.crypto.aes.AesCryptoModule @@ -146,6 +147,7 @@ object ExperiencePackagePicker : ModulesProvider { CryptoModule::class.java to null, ConstantsModule::class.java to null, ContactsModule::class.java to null, + ContactsNextModule::class.java to null, DeviceModule::class.java to null, DocumentPickerModule::class.java to null, EASClientModule::class.java to null, diff --git a/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx b/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx index 31d856709d8993..e0c306318645ec 100644 --- a/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx +++ b/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx @@ -17,6 +17,7 @@ import { CryptoScreens } from '../screens/Crypto/CryptoScreen'; import ExpoApis from '../screens/ExpoApisScreen'; import { MediaLibraryScreens } from '../screens/MediaLibrary@Next/MediaLibraryScreens'; import { ModulesCoreScreens } from '../screens/ModulesCore/ModulesCoreScreen'; +import { WorkletsScreens } from '../screens/Worklets/WorkletsScreen'; import { type ScreenConfig } from '../types/ScreenConfig'; const Stack = createNativeStackNavigator(); @@ -446,6 +447,13 @@ export const ScreensList: ScreenConfig[] = [ }, name: 'Updates Reload Screen', }, + { + getComponent() { + return optionalRequire(() => require('../screens/Worklets/WorkletsScreen')); + }, + name: 'Worklets integration', + route: 'worklets', + }, { getComponent() { return optionalRequire(() => require('../screens/WebBrowser/WebBrowserScreen')); @@ -476,6 +484,7 @@ export const Screens: ScreenConfig[] = [ ...CalendarsScreens, ...CalendarsNextScreens, ...CryptoScreens, + ...WorkletsScreens, ]; export const screenApiItems = apiScreensToListElements(ScreensList); diff --git a/apps/native-component-list/src/screens/Worklets/WorkletsInitScreen.tsx b/apps/native-component-list/src/screens/Worklets/WorkletsInitScreen.tsx new file mode 100644 index 00000000000000..95cfe4d951ce5c --- /dev/null +++ b/apps/native-component-list/src/screens/Worklets/WorkletsInitScreen.tsx @@ -0,0 +1,95 @@ +import { installOnUIRuntime } from 'expo'; +import React, { useEffect, useState } from 'react'; +import { View, StyleSheet, Text } from 'react-native'; +import { runOnJS, runOnUI } from 'react-native-worklets'; +import 'react-native-reanimated'; + +installOnUIRuntime(); + +export default function WorkletsInitScreen() { + const [isExpoObjectAvailable, setIsExpoObjectAvailable] = useState(false); + const [isEventEmitterAvailable, setIsEventEmitterAvailable] = useState(false); + const [isNativeModuleAvailable, setIsNativeModuleAvailable] = useState(false); + + useEffect(() => { + runOnUI(() => { + runOnJS(setIsExpoObjectAvailable)(!!globalThis.expo); + runOnJS(setIsEventEmitterAvailable)(!!globalThis.expo?.EventEmitter); + runOnJS(setIsNativeModuleAvailable)(!!globalThis.expo?.NativeModule); + })(); + }, []); + + return ( + + + Worklets UI Runtime Status + + + + + + + ); +} + +function StatusRow({ label, available }: { label: string; available: boolean }) { + return ( + + {label}: + + {available ? '✓ Available' : '✗ Unavailable'} + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + backgroundColor: '#f5f5f5', + }, + section: { + marginBottom: 20, + }, + title: { + fontSize: 20, + fontWeight: '600', + color: '#333', + }, + row: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 12, + paddingHorizontal: 16, + backgroundColor: '#fff', + borderRadius: 8, + marginBottom: 8, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.1, + shadowRadius: 2, + elevation: 2, + }, + label: { + fontSize: 16, + fontWeight: '500', + color: '#333', + flex: 1, + }, + status: { + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 6, + }, + statusAvailable: { + backgroundColor: '#d4edda', + }, + statusUnavailable: { + backgroundColor: '#f8d7da', + }, + statusText: { + fontSize: 14, + fontWeight: '500', + }, +}); diff --git a/apps/native-component-list/src/screens/Worklets/WorkletsScreen.tsx b/apps/native-component-list/src/screens/Worklets/WorkletsScreen.tsx new file mode 100644 index 00000000000000..50b0cc4b2ea831 --- /dev/null +++ b/apps/native-component-list/src/screens/Worklets/WorkletsScreen.tsx @@ -0,0 +1,17 @@ +import { optionalRequire } from '../../navigation/routeBuilder'; +import ComponentListScreen, { apiScreensToListElements } from '../ComponentListScreen'; + +export const WorkletsScreens = [ + { + name: 'Worklets Initialization', + route: 'worklets/init', + getComponent() { + return optionalRequire(() => require('./WorkletsInitScreen')); + }, + }, +]; + +export default function WorkletsScreen() { + const apis = apiScreensToListElements(WorkletsScreens); + return ; +} diff --git a/apps/router-e2e/__e2e__/native-navigation/app/header-items.tsx b/apps/router-e2e/__e2e__/native-navigation/app/header-items.tsx index 7abbe314ce7115..5eb03739f4862a 100644 --- a/apps/router-e2e/__e2e__/native-navigation/app/header-items.tsx +++ b/apps/router-e2e/__e2e__/native-navigation/app/header-items.tsx @@ -99,225 +99,221 @@ export default function HeaderItemsScreen() { return ( <> - - + 99 + + + {/* Simple actions */} + + Send email + + + + Delete email + + + + {/* Toggle action */} + + + {emailsArchived ? 'Unarchive emails' : 'Archive emails'} + + + + + {/* Nested inline menu - View mode */} + + View Mode + handleViewModeSelect('icons')}> + Icons + + + handleViewModeSelect('list')}> + List + + + + + {/* Nested inline menu - Sort by */} + + Sort By + handleSortBySelect('name')}> + Name + + handleSortBySelect('kind')}> + Kind + + handleSortBySelect('date')}> + Date + + handleSortBySelect('size')}> + Size + + handleSortBySelect('tags')}> + Tags + + + + {/* Nested menu - Preferences */} + + + + {notificationsEnabled ? 'Disable notifications' : 'Enable notifications'} + + + + + {/* Color selection submenu */} + + handleColorSelect('red')}> + Red + + + handleColorSelect('blue')}> + Blue + + + handleColorSelect('green')}> + Green + + + + + + {/* Palette menu */} + + + Alert.alert('Star')}> + Star + + Alert.alert('Flag')}> + Flag + + Alert.alert('Pin')}> + Pin + + + + {/* Disabled action */} + {}}> + Locked action + + + + + + + + + - Form Sheet Modal Content - Start - Form Sheet Modal Content - End - + <> + + + console.log('Checkmark pressed')} /> + + + console.log('Xmark pressed')} /> + + + Form Sheet Modal Content - Start + Form Sheet Modal Content - End + + ); } diff --git a/apps/router-e2e/__e2e__/native-navigation/app/modals/index.tsx b/apps/router-e2e/__e2e__/native-navigation/app/modals/index.tsx index a94fbc4f054916..b1be667de2fa7d 100644 --- a/apps/router-e2e/__e2e__/native-navigation/app/modals/index.tsx +++ b/apps/router-e2e/__e2e__/native-navigation/app/modals/index.tsx @@ -1,4 +1,4 @@ -import { Link, usePathname, type Href } from 'expo-router'; +import { Link, Stack, usePathname, type Href } from 'expo-router'; import React from 'react'; import { Text, Pressable, ScrollView, View } from 'react-native'; import { featureFlags } from 'react-native-screens'; @@ -11,6 +11,16 @@ const HomeIndex = () => { style={{ flex: 1, backgroundColor: '#fff' }} contentContainerStyle={{ alignItems: 'center', gap: 16 }} contentInsetAdjustmentBehavior="automatic"> + + + console.log('Index Checkmark pressed')} + /> + + + console.log('Index Xmark pressed')} /> + Modals Current Path: {pathname} diff --git a/apps/router-e2e/__e2e__/native-navigation/app/toolbar.tsx b/apps/router-e2e/__e2e__/native-navigation/app/toolbar.tsx index dec13634a489e5..80743800998ac1 100644 --- a/apps/router-e2e/__e2e__/native-navigation/app/toolbar.tsx +++ b/apps/router-e2e/__e2e__/native-navigation/app/toolbar.tsx @@ -1,6 +1,5 @@ import { useImage } from 'expo-image'; import { Color, Stack, useLocalSearchParams } from 'expo-router'; -import { Toolbar } from 'expo-router/unstable-toolbar'; import { SymbolView } from 'expo-symbols'; import { useState, useRef } from 'react'; import { @@ -101,7 +100,6 @@ export default function ToolbarScreen() { contentContainerStyle={styles.contentContainer} contentInsetAdjustmentBehavior="automatic"> Toolbar E2E Test Screen - setIsSearchFocused(true)} onBlur={() => setIsSearchFocused(false)} @@ -271,19 +269,19 @@ export default function ToolbarScreen() { - + {/* Flexible spacer at the start */} - + {/* Search bar */} - + + ); } diff --git a/apps/router-e2e/__e2e__/stack/app/[id].tsx b/apps/router-e2e/__e2e__/stack/app/[id].tsx index 372ed89d0a4437..aa998459409449 100644 --- a/apps/router-e2e/__e2e__/stack/app/[id].tsx +++ b/apps/router-e2e/__e2e__/stack/app/[id].tsx @@ -1,5 +1,4 @@ import { Color, Link, Stack, useRouter } from 'expo-router'; -import { Toolbar } from 'expo-router/unstable-toolbar'; import { useRef, useState } from 'react'; import { Button, Text, View } from 'react-native'; import type { SearchBarCommands } from 'react-native-screens'; @@ -24,27 +23,27 @@ export default function Modal() {