@expo/ui 56.0.8 → 56.0.10
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/CHANGELOG.md +35 -1
- package/CLAUDE.md +1 -1
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +49 -6
- package/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt +97 -16
- package/android/src/main/java/expo/modules/ui/LoadingView.kt +80 -0
- package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +31 -3
- package/android/src/main/java/expo/modules/ui/SnackbarView.kt +126 -0
- package/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt +2 -0
- package/android/src/main/java/expo/modules/ui/state/ObservableState.kt +10 -0
- package/assets/keyboard_arrow_down.xml +10 -0
- package/build/State/index.d.ts +6 -0
- package/build/State/index.d.ts.map +1 -0
- package/build/State/useNativeState.d.ts +32 -3
- package/build/State/useNativeState.d.ts.map +1 -1
- package/build/community/bottom-sheet/BottomSheet.ios.d.ts.map +1 -1
- package/build/community/pager-view/PagerView.android.d.ts +7 -0
- package/build/community/pager-view/PagerView.android.d.ts.map +1 -0
- package/build/community/pager-view/PagerView.d.ts +8 -0
- package/build/community/pager-view/PagerView.d.ts.map +1 -0
- package/build/community/pager-view/PagerView.ios.d.ts +15 -0
- package/build/community/pager-view/PagerView.ios.d.ts.map +1 -0
- package/build/community/pager-view/index.d.ts +3 -0
- package/build/community/pager-view/index.d.ts.map +1 -0
- package/build/community/pager-view/types.d.ts +128 -0
- package/build/community/pager-view/types.d.ts.map +1 -0
- package/build/community/segmented-control/vendor/SegmentsSeparators.d.ts.map +1 -1
- package/build/jetpack-compose/HorizontalPager/index.d.ts +27 -0
- package/build/jetpack-compose/HorizontalPager/index.d.ts.map +1 -1
- package/build/jetpack-compose/Host/index.d.ts +1 -0
- package/build/jetpack-compose/Host/index.d.ts.map +1 -1
- package/build/jetpack-compose/LoadingIndicator/index.d.ts +41 -0
- package/build/jetpack-compose/LoadingIndicator/index.d.ts.map +1 -0
- package/build/jetpack-compose/Snackbar/index.d.ts +94 -0
- package/build/jetpack-compose/Snackbar/index.d.ts.map +1 -0
- package/build/jetpack-compose/SyncSwitch/index.d.ts +1 -1
- package/build/jetpack-compose/SyncSwitch/index.d.ts.map +1 -1
- package/build/jetpack-compose/TextField/index.d.ts +1 -1
- package/build/jetpack-compose/TextField/index.d.ts.map +1 -1
- package/build/jetpack-compose/index.d.ts +3 -2
- package/build/jetpack-compose/index.d.ts.map +1 -1
- package/build/jetpack-compose/modifiers/index.d.ts +6 -2
- package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/BottomSheet/index.d.ts +5 -1
- package/build/swift-ui/BottomSheet/index.d.ts.map +1 -1
- package/build/swift-ui/Host/index.d.ts +1 -0
- package/build/swift-ui/Host/index.d.ts.map +1 -1
- package/build/swift-ui/ScrollView/index.d.ts +30 -0
- package/build/swift-ui/ScrollView/index.d.ts.map +1 -1
- package/build/swift-ui/SecureField/index.d.ts +1 -1
- package/build/swift-ui/SecureField/index.d.ts.map +1 -1
- package/build/swift-ui/SyncToggle/index.d.ts +1 -1
- package/build/swift-ui/SyncToggle/index.d.ts.map +1 -1
- package/build/swift-ui/TextField/index.d.ts +1 -1
- package/build/swift-ui/TextField/index.d.ts.map +1 -1
- package/build/swift-ui/index.d.ts +2 -2
- package/build/swift-ui/index.d.ts.map +1 -1
- package/build/swift-ui/modifiers/index.d.ts +25 -15
- package/build/swift-ui/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/modifiers/scrollObservation.d.ts +52 -0
- package/build/swift-ui/modifiers/scrollObservation.d.ts.map +1 -0
- package/build/swift-ui/modifiers/scrollPosition.d.ts +1 -1
- package/build/swift-ui/modifiers/scrollPosition.d.ts.map +1 -1
- package/build/swift-ui/modifiers/symbolEffect.d.ts +1 -1
- package/build/swift-ui/modifiers/symbolEffect.d.ts.map +1 -1
- package/build/swift-ui/withAnimation.d.ts +26 -0
- package/build/swift-ui/withAnimation.d.ts.map +1 -0
- package/build/universal/BottomSheet/index.android.d.ts +1 -1
- package/build/universal/BottomSheet/index.android.d.ts.map +1 -1
- package/build/universal/BottomSheet/index.d.ts +1 -1
- package/build/universal/BottomSheet/index.d.ts.map +1 -1
- package/build/universal/BottomSheet/index.ios.d.ts +1 -1
- package/build/universal/BottomSheet/index.ios.d.ts.map +1 -1
- package/build/universal/BottomSheet/types.d.ts +27 -0
- package/build/universal/BottomSheet/types.d.ts.map +1 -1
- package/build/universal/Checkbox/index.d.ts.map +1 -1
- package/build/universal/Collapsible/index.android.d.ts +8 -0
- package/build/universal/Collapsible/index.android.d.ts.map +1 -0
- package/build/universal/Collapsible/index.d.ts +8 -0
- package/build/universal/Collapsible/index.d.ts.map +1 -0
- package/build/universal/Collapsible/index.ios.d.ts +7 -0
- package/build/universal/Collapsible/index.ios.d.ts.map +1 -0
- package/build/universal/Collapsible/types.d.ts +23 -0
- package/build/universal/Collapsible/types.d.ts.map +1 -0
- package/build/universal/Column/index.d.ts.map +1 -1
- package/build/universal/Host/index.d.ts +5 -18
- package/build/universal/Host/index.d.ts.map +1 -1
- package/build/universal/Host/types.d.ts +72 -0
- package/build/universal/Host/types.d.ts.map +1 -0
- package/build/universal/List/index.android.d.ts +9 -0
- package/build/universal/List/index.android.d.ts.map +1 -0
- package/build/universal/List/index.d.ts +8 -0
- package/build/universal/List/index.d.ts.map +1 -0
- package/build/universal/List/index.ios.d.ts +8 -0
- package/build/universal/List/index.ios.d.ts.map +1 -0
- package/build/universal/List/types.d.ts +26 -0
- package/build/universal/List/types.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.android.d.ts +8 -0
- package/build/universal/ListItem/ListItem.android.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.d.ts +9 -0
- package/build/universal/ListItem/ListItem.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.ios.d.ts +8 -0
- package/build/universal/ListItem/ListItem.ios.d.ts.map +1 -0
- package/build/universal/ListItem/ListItemSlots.d.ts +21 -0
- package/build/universal/ListItem/ListItemSlots.d.ts.map +1 -0
- package/build/universal/ListItem/index.d.ts +10 -0
- package/build/universal/ListItem/index.d.ts.map +1 -0
- package/build/universal/ListItem/types.d.ts +59 -0
- package/build/universal/ListItem/types.d.ts.map +1 -0
- package/build/universal/Picker/Picker.android.d.ts +9 -0
- package/build/universal/Picker/Picker.android.d.ts.map +1 -0
- package/build/universal/Picker/Picker.d.ts +8 -0
- package/build/universal/Picker/Picker.d.ts.map +1 -0
- package/build/universal/Picker/Picker.ios.d.ts +9 -0
- package/build/universal/Picker/Picker.ios.d.ts.map +1 -0
- package/build/universal/Picker/PickerItem.d.ts +9 -0
- package/build/universal/Picker/PickerItem.d.ts.map +1 -0
- package/build/universal/Picker/index.d.ts +8 -0
- package/build/universal/Picker/index.d.ts.map +1 -0
- package/build/universal/Picker/types.d.ts +69 -0
- package/build/universal/Picker/types.d.ts.map +1 -0
- package/build/universal/RNHostView/index.android.d.ts +7 -0
- package/build/universal/RNHostView/index.android.d.ts.map +1 -0
- package/build/universal/RNHostView/index.d.ts +7 -0
- package/build/universal/RNHostView/index.d.ts.map +1 -0
- package/build/universal/RNHostView/index.ios.d.ts +7 -0
- package/build/universal/RNHostView/index.ios.d.ts.map +1 -0
- package/build/universal/RNHostView/types.d.ts +23 -0
- package/build/universal/RNHostView/types.d.ts.map +1 -0
- package/build/universal/Switch/index.d.ts.map +1 -1
- package/build/universal/TextInput/index.d.ts.map +1 -1
- package/build/universal/index.d.ts +5 -0
- package/build/universal/index.d.ts.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/BottomSheetView.swift +4 -1
- package/ios/ExpoUIModule.swift +47 -4
- package/ios/HostView.swift +21 -18
- package/ios/Modifiers/AnimationConfig.swift +109 -0
- package/ios/Modifiers/FontModifier.swift +72 -20
- package/ios/Modifiers/ScrollObservationModifiers.swift +107 -0
- package/ios/Modifiers/ViewModifierRegistry.swift +9 -112
- package/ios/State/ObservableState.swift +12 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8-sources.jar → 56.0.10/expo.modules.ui-56.0.10-sources.jar} +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8.module → 56.0.10/expo.modules.ui-56.0.10.module} +22 -22
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8.pom → 56.0.10/expo.modules.ui-56.0.10.pom} +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
- package/package.json +7 -3
- package/src/State/index.ts +10 -0
- package/src/State/useNativeState.ts +71 -3
- package/src/community/bottom-sheet/BottomSheet.ios.tsx +0 -17
- package/src/community/pager-view/PagerView.android.tsx +223 -0
- package/src/community/pager-view/PagerView.ios.tsx +267 -0
- package/src/community/pager-view/PagerView.tsx +14 -0
- package/src/community/pager-view/index.tsx +13 -0
- package/src/community/pager-view/types.tsx +137 -0
- package/src/community/picker/Picker.android.tsx +1 -1
- package/src/community/segmented-control/vendor/SegmentsSeparators.tsx +3 -6
- package/src/jetpack-compose/HorizontalPager/index.tsx +89 -18
- package/src/jetpack-compose/Host/index.tsx +1 -0
- package/src/jetpack-compose/LoadingIndicator/index.tsx +91 -0
- package/src/jetpack-compose/Snackbar/index.tsx +135 -0
- package/src/jetpack-compose/SyncSwitch/index.tsx +1 -3
- package/src/jetpack-compose/TextField/index.tsx +1 -4
- package/src/jetpack-compose/index.ts +3 -2
- package/src/jetpack-compose/modifiers/index.ts +5 -2
- package/src/swift-ui/BottomSheet/index.tsx +32 -15
- package/src/swift-ui/Host/index.tsx +1 -0
- package/src/swift-ui/ScrollView/index.tsx +33 -0
- package/src/swift-ui/SecureField/index.tsx +1 -4
- package/src/swift-ui/SyncToggle/index.tsx +1 -3
- package/src/swift-ui/TextField/index.tsx +1 -4
- package/src/swift-ui/index.tsx +2 -3
- package/src/swift-ui/modifiers/index.ts +37 -14
- package/src/swift-ui/modifiers/scrollObservation.ts +80 -0
- package/src/swift-ui/modifiers/scrollPosition.ts +1 -2
- package/src/swift-ui/modifiers/symbolEffect.ts +1 -2
- package/src/swift-ui/withAnimation.ts +71 -0
- package/src/ts-declarations/react-native-web.d.ts +7 -0
- package/src/universal/BottomSheet/index.android.tsx +58 -11
- package/src/universal/BottomSheet/index.ios.tsx +40 -20
- package/src/universal/BottomSheet/index.tsx +49 -4
- package/src/universal/BottomSheet/types.ts +25 -0
- package/src/universal/Checkbox/index.tsx +14 -2
- package/src/universal/Collapsible/index.android.tsx +72 -0
- package/src/universal/Collapsible/index.ios.tsx +16 -0
- package/src/universal/Collapsible/index.tsx +71 -0
- package/src/universal/Collapsible/types.ts +25 -0
- package/src/universal/Column/index.tsx +3 -1
- package/src/universal/Host/index.tsx +9 -10
- package/src/universal/Host/types.ts +70 -0
- package/src/universal/List/index.android.tsx +44 -0
- package/src/universal/List/index.ios.tsx +19 -0
- package/src/universal/List/index.tsx +26 -0
- package/src/universal/List/types.ts +28 -0
- package/src/universal/ListItem/ListItem.android.tsx +52 -0
- package/src/universal/ListItem/ListItem.ios.tsx +58 -0
- package/src/universal/ListItem/ListItem.tsx +77 -0
- package/src/universal/ListItem/ListItemSlots.tsx +66 -0
- package/src/universal/ListItem/index.ts +15 -0
- package/src/universal/ListItem/types.ts +67 -0
- package/src/universal/Picker/Picker.android.tsx +69 -0
- package/src/universal/Picker/Picker.ios.tsx +45 -0
- package/src/universal/Picker/Picker.tsx +52 -0
- package/src/universal/Picker/PickerItem.tsx +27 -0
- package/src/universal/Picker/index.ts +11 -0
- package/src/universal/Picker/types.ts +79 -0
- package/src/universal/RNHostView/index.android.tsx +33 -0
- package/src/universal/RNHostView/index.ios.tsx +12 -0
- package/src/universal/RNHostView/index.tsx +39 -0
- package/src/universal/RNHostView/types.ts +25 -0
- package/src/universal/Switch/index.tsx +7 -2
- package/src/universal/TextInput/index.tsx +12 -2
- package/src/universal/index.ts +5 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha512 +0 -1
- package/src/community/bottom-sheet/CLAUDE.md +0 -55
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
isValidElement,
|
|
4
|
+
useEffect,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
type ReactElement,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { Platform } from 'react-native';
|
|
13
|
+
|
|
14
|
+
import { wrapNativeEvent, type PagerViewProps } from './types';
|
|
15
|
+
import { useNativeState, worklets } from '../../State';
|
|
16
|
+
import { Group } from '../../swift-ui/Group';
|
|
17
|
+
import { Host } from '../../swift-ui/Host';
|
|
18
|
+
import { LazyHStack } from '../../swift-ui/LazyHStack';
|
|
19
|
+
import { RNHostView } from '../../swift-ui/RNHostView';
|
|
20
|
+
import { ScrollView, type ScrollGeometry, type ScrollPhase } from '../../swift-ui/ScrollView';
|
|
21
|
+
import {
|
|
22
|
+
containerRelativeFrame,
|
|
23
|
+
id,
|
|
24
|
+
onScrollPhaseChange,
|
|
25
|
+
scrollDisabled,
|
|
26
|
+
scrollPosition,
|
|
27
|
+
scrollTargetBehavior,
|
|
28
|
+
scrollTargetLayout,
|
|
29
|
+
useScrollGeometryChange,
|
|
30
|
+
} from '../../swift-ui/modifiers';
|
|
31
|
+
import { Animation } from '../../swift-ui/modifiers/animation';
|
|
32
|
+
import { withAnimation } from '../../swift-ui/withAnimation';
|
|
33
|
+
|
|
34
|
+
function phaseToPageState(phase: ScrollPhase): 'idle' | 'dragging' | 'settling' {
|
|
35
|
+
switch (phase) {
|
|
36
|
+
case 'idle':
|
|
37
|
+
return 'idle';
|
|
38
|
+
case 'tracking':
|
|
39
|
+
case 'interacting':
|
|
40
|
+
return 'dragging';
|
|
41
|
+
case 'animating':
|
|
42
|
+
case 'decelerating':
|
|
43
|
+
return 'settling';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Drop-in replacement for `react-native-pager-view` on iOS.
|
|
49
|
+
*
|
|
50
|
+
* Renders a SwiftUI `ScrollView` with paging behavior. Scroll position is the
|
|
51
|
+
* single source of truth: an `ObservableState` bound through the
|
|
52
|
+
* `scrollPosition` modifier drives initial placement, user-swipe writeback,
|
|
53
|
+
* and imperative `setPage` / `setPageWithoutAnimation`. The animated path
|
|
54
|
+
* routes the write through `withAnimation`; if `react-native-worklets` isn't
|
|
55
|
+
* installed, `setPage` falls back to a non-animated jump. Requires iOS 17+.
|
|
56
|
+
* Continuous progress (`onPageScroll`) and scroll state
|
|
57
|
+
* (`onPageScrollStateChanged`) events require iOS 18+.
|
|
58
|
+
*/
|
|
59
|
+
export function PagerView(props: PagerViewProps) {
|
|
60
|
+
const {
|
|
61
|
+
ref,
|
|
62
|
+
initialPage = 0,
|
|
63
|
+
scrollEnabled = true,
|
|
64
|
+
onPageScroll,
|
|
65
|
+
onPageScrollStateChanged,
|
|
66
|
+
onPageSelected,
|
|
67
|
+
children,
|
|
68
|
+
style,
|
|
69
|
+
} = props;
|
|
70
|
+
|
|
71
|
+
warnIfPreIOS18ScrollCallbacksDropped(onPageScroll, onPageScrollStateChanged);
|
|
72
|
+
|
|
73
|
+
const [scrollEnabledState, setScrollEnabledState] = useState(scrollEnabled);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
setScrollEnabledState(scrollEnabled);
|
|
76
|
+
}, [scrollEnabled]);
|
|
77
|
+
|
|
78
|
+
const validChildren = useMemo(
|
|
79
|
+
() => Children.toArray(children).filter((c): c is ReactElement => isValidElement(c)),
|
|
80
|
+
[children]
|
|
81
|
+
);
|
|
82
|
+
const pageCount = validChildren.length;
|
|
83
|
+
const pageCountRef = useRef(0);
|
|
84
|
+
pageCountRef.current = pageCount;
|
|
85
|
+
|
|
86
|
+
// Clamp on first render — out-of-range initials produce an id that matches
|
|
87
|
+
// no `Group`, leaving `.scrollPosition(id:)` silently stuck at 0.
|
|
88
|
+
const [clampedInitialPage] = useState(() => {
|
|
89
|
+
if (pageCount === 0) return 0;
|
|
90
|
+
return Math.max(0, Math.min(pageCount - 1, initialPage));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const activePageState = useNativeState<string | null>(
|
|
94
|
+
clampedInitialPage > 0 ? String(clampedInitialPage) : null
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// The SwiftUI writeback fires for both user swipes and our own imperative
|
|
98
|
+
// writes; dedup against this ref so `onPageSelected` fires once per change.
|
|
99
|
+
const lastSelectedPageRef = useRef(clampedInitialPage);
|
|
100
|
+
|
|
101
|
+
// Read the latest `onPageSelected` through a ref so the shrink-clamp effect
|
|
102
|
+
// doesn't need it in deps (and won't re-run / double-fire on callback
|
|
103
|
+
// identity changes).
|
|
104
|
+
const onPageSelectedRef = useRef(onPageSelected);
|
|
105
|
+
onPageSelectedRef.current = onPageSelected;
|
|
106
|
+
|
|
107
|
+
const handleScrolledIDChange = (newId: string | null) => {
|
|
108
|
+
if (newId == null) return;
|
|
109
|
+
const page = parseInt(newId, 10);
|
|
110
|
+
if (!Number.isFinite(page)) return;
|
|
111
|
+
if (page !== lastSelectedPageRef.current) {
|
|
112
|
+
lastSelectedPageRef.current = page;
|
|
113
|
+
onPageSelectedRef.current?.(wrapNativeEvent({ position: page }));
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Without clamping, an out-of-range id would silently no-op on
|
|
118
|
+
// `.scrollPosition(id:)` instead of jumping to the nearest page.
|
|
119
|
+
const clampPage = (page: number): number | null => {
|
|
120
|
+
const count = pageCountRef.current;
|
|
121
|
+
if (count === 0 || !Number.isFinite(page)) return null;
|
|
122
|
+
return Math.max(0, Math.min(count - 1, page));
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Bypasses the public `value` setter on JS-thread writes — its dev warning
|
|
126
|
+
// is aimed at user code; our own imperative API is an intentional JS-thread
|
|
127
|
+
// writer. Inside a worklet we use `activePageState.value = …` directly,
|
|
128
|
+
// which routes through the SharedObject prototype installed by `index.fx`.
|
|
129
|
+
const writePageFromJS = (id: string) => {
|
|
130
|
+
(activePageState as unknown as { setValue(v: { value: string }): void }).setValue({
|
|
131
|
+
value: id,
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Re-anchor when the page count drops past the current selection. Matches
|
|
136
|
+
// Android, where Compose's `PagerState` clamps `currentPage` to the new
|
|
137
|
+
// max and `settledPage` fires the corresponding event. Without this on
|
|
138
|
+
// iOS, `.scrollPosition(id:)` silently no-ops on the missing id and the
|
|
139
|
+
// pager visually drifts without firing `onPageSelected`.
|
|
140
|
+
useLayoutEffect(() => {
|
|
141
|
+
if (pageCount === 0) return;
|
|
142
|
+
if (lastSelectedPageRef.current < pageCount) return;
|
|
143
|
+
const clamped = pageCount - 1;
|
|
144
|
+
lastSelectedPageRef.current = clamped;
|
|
145
|
+
writePageFromJS(String(clamped));
|
|
146
|
+
onPageSelectedRef.current?.(wrapNativeEvent({ position: clamped }));
|
|
147
|
+
}, [pageCount]);
|
|
148
|
+
|
|
149
|
+
useImperativeHandle(
|
|
150
|
+
ref,
|
|
151
|
+
() => ({
|
|
152
|
+
setPage: (page: number) => {
|
|
153
|
+
const clamped = clampPage(page);
|
|
154
|
+
if (clamped == null) return;
|
|
155
|
+
const nextId = String(clamped);
|
|
156
|
+
// `withAnimation` requires `react-native-worklets`; fall back to an
|
|
157
|
+
// instant jump if it isn't installed.
|
|
158
|
+
if (worklets) {
|
|
159
|
+
// `null` would disable animation; `Animation.default` opts into
|
|
160
|
+
// SwiftUI's default paging animation.
|
|
161
|
+
withAnimation(Animation.default, () => {
|
|
162
|
+
'worklet';
|
|
163
|
+
activePageState.value = nextId;
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
writePageFromJS(nextId);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
setPageWithoutAnimation: (page: number) => {
|
|
170
|
+
const clamped = clampPage(page);
|
|
171
|
+
if (clamped == null) return;
|
|
172
|
+
writePageFromJS(String(clamped));
|
|
173
|
+
},
|
|
174
|
+
setScrollEnabled: setScrollEnabledState,
|
|
175
|
+
}),
|
|
176
|
+
[activePageState]
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const handleScrollGeometry = useMemo(
|
|
180
|
+
() => (onPageScroll ? buildHandleScrollGeometry(onPageScroll) : undefined),
|
|
181
|
+
[onPageScroll]
|
|
182
|
+
);
|
|
183
|
+
const geometryModifier = useScrollGeometryChange(handleScrollGeometry);
|
|
184
|
+
|
|
185
|
+
const phaseModifier = onPageScrollStateChanged
|
|
186
|
+
? onScrollPhaseChange((phase) =>
|
|
187
|
+
onPageScrollStateChanged(wrapNativeEvent({ pageScrollState: phaseToPageState(phase) }))
|
|
188
|
+
)
|
|
189
|
+
: null;
|
|
190
|
+
|
|
191
|
+
const pages = validChildren.map((child, index) => (
|
|
192
|
+
<Group
|
|
193
|
+
key={child.key ?? String(index)}
|
|
194
|
+
modifiers={[containerRelativeFrame({ axes: 'horizontal' }), id(String(index))]}>
|
|
195
|
+
<RNHostView>{child}</RNHostView>
|
|
196
|
+
</Group>
|
|
197
|
+
));
|
|
198
|
+
|
|
199
|
+
// Toggle the flag rather than splicing the modifier in/out — SwiftUI diffs
|
|
200
|
+
// modifiers by position, so a shifting array resets the ScrollView's content.
|
|
201
|
+
const modifiers = [
|
|
202
|
+
scrollTargetBehavior('paging'),
|
|
203
|
+
scrollDisabled(!scrollEnabledState),
|
|
204
|
+
scrollPosition(activePageState, { onChange: handleScrolledIDChange }),
|
|
205
|
+
...(geometryModifier ? [geometryModifier] : []),
|
|
206
|
+
...(phaseModifier ? [phaseModifier] : []),
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<Host style={style ?? { flex: 1 }}>
|
|
211
|
+
<ScrollView axes="horizontal" showsIndicators={false} modifiers={modifiers}>
|
|
212
|
+
<LazyHStack spacing={0} modifiers={[scrollTargetLayout()]}>
|
|
213
|
+
{pages}
|
|
214
|
+
</LazyHStack>
|
|
215
|
+
</ScrollView>
|
|
216
|
+
</Host>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function geometryToPageScroll(geometry: ScrollGeometry): { position: number; offset: number } {
|
|
221
|
+
'worklet';
|
|
222
|
+
// Clamp so rubber-band overscroll doesn't emit `position` outside
|
|
223
|
+
// `[0, pageCount - 1]`. `Math.round` tolerates sub-pixel content sizing.
|
|
224
|
+
const pageCount = Math.max(1, Math.round(geometry.contentWidth / geometry.containerWidth));
|
|
225
|
+
const positionFloat = Math.max(
|
|
226
|
+
0,
|
|
227
|
+
Math.min(pageCount - 1, geometry.contentOffsetX / geometry.containerWidth)
|
|
228
|
+
);
|
|
229
|
+
const position = Math.floor(positionFloat);
|
|
230
|
+
return { position, offset: positionFloat - position };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Mirrors the worklet-ness of the user's callback so the per-frame mapping
|
|
234
|
+
// stays on the UI runtime when the user is also on it.
|
|
235
|
+
function buildHandleScrollGeometry(
|
|
236
|
+
userOnPageScroll: NonNullable<PagerViewProps['onPageScroll']>
|
|
237
|
+
): (geometry: ScrollGeometry) => void {
|
|
238
|
+
if (worklets?.isWorkletFunction?.(userOnPageScroll)) {
|
|
239
|
+
return (geometry) => {
|
|
240
|
+
'worklet';
|
|
241
|
+
if (geometry.containerWidth <= 0) return;
|
|
242
|
+
userOnPageScroll(wrapNativeEvent(geometryToPageScroll(geometry)));
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return (geometry) => {
|
|
246
|
+
if (geometry.containerWidth <= 0) return;
|
|
247
|
+
userOnPageScroll(wrapNativeEvent(geometryToPageScroll(geometry)));
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let didWarnPreIOS18 = false;
|
|
252
|
+
function warnIfPreIOS18ScrollCallbacksDropped(
|
|
253
|
+
onPageScroll: PagerViewProps['onPageScroll'],
|
|
254
|
+
onPageScrollStateChanged: PagerViewProps['onPageScrollStateChanged']
|
|
255
|
+
) {
|
|
256
|
+
if (!__DEV__ || didWarnPreIOS18) return;
|
|
257
|
+
if (!onPageScroll && !onPageScrollStateChanged) return;
|
|
258
|
+
const major = parseInt(String(Platform.Version), 10);
|
|
259
|
+
if (Number.isFinite(major) && major < 18) {
|
|
260
|
+
didWarnPreIOS18 = true;
|
|
261
|
+
console.warn(
|
|
262
|
+
`[expo-ui PagerView] onPageScroll and onPageScrollStateChanged require iOS 18+ ` +
|
|
263
|
+
`and will not fire on iOS ${Platform.Version}. Guard with Platform.Version or ` +
|
|
264
|
+
`provide a fallback for older iOS targets.`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import type { PagerViewProps } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A drop-in replacement for `react-native-pager-view`. Renders a horizontally
|
|
7
|
+
* paged view backed by Jetpack Compose's `HorizontalPager` on Android and
|
|
8
|
+
* SwiftUI on iOS. Each child is treated as a separate page.
|
|
9
|
+
*/
|
|
10
|
+
export function PagerView(_props: PagerViewProps): never {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`@expo/ui/community/pager-view is not implemented on ${Platform.OS}. Supported platforms: android, ios.`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
PagerViewProps,
|
|
3
|
+
PagerViewRef,
|
|
4
|
+
PagerViewOnPageScrollEventData,
|
|
5
|
+
PagerViewOnPageSelectedEventData,
|
|
6
|
+
PageScrollStateChangedEventData,
|
|
7
|
+
PagerViewOnPageScrollEvent,
|
|
8
|
+
PagerViewOnPageSelectedEvent,
|
|
9
|
+
PageScrollStateChangedEvent,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
// named export is for the docs generator; default for normal consumption
|
|
13
|
+
export { PagerView, PagerView as default } from './PagerView';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { ReactNode, Ref } from 'react';
|
|
2
|
+
import type { NativeSyntheticEvent, ViewProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Event payload for `onPageScroll`. Mirrors the upstream
|
|
6
|
+
* `react-native-pager-view` shape.
|
|
7
|
+
*/
|
|
8
|
+
export type PagerViewOnPageScrollEventData = Readonly<{
|
|
9
|
+
position: number;
|
|
10
|
+
offset: number;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Event payload for `onPageSelected`. Mirrors the upstream
|
|
15
|
+
* `react-native-pager-view` shape.
|
|
16
|
+
*/
|
|
17
|
+
export type PagerViewOnPageSelectedEventData = Readonly<{
|
|
18
|
+
position: number;
|
|
19
|
+
}>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Event payload for `onPageScrollStateChanged`. Mirrors the upstream
|
|
23
|
+
* `react-native-pager-view` shape.
|
|
24
|
+
*/
|
|
25
|
+
export type PageScrollStateChangedEventData = Readonly<{
|
|
26
|
+
pageScrollState: 'idle' | 'dragging' | 'settling';
|
|
27
|
+
}>;
|
|
28
|
+
|
|
29
|
+
export type PagerViewOnPageScrollEvent = NativeSyntheticEvent<PagerViewOnPageScrollEventData>;
|
|
30
|
+
export type PagerViewOnPageSelectedEvent = NativeSyntheticEvent<PagerViewOnPageSelectedEventData>;
|
|
31
|
+
export type PageScrollStateChangedEvent = NativeSyntheticEvent<PageScrollStateChangedEventData>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Wraps a payload as `NativeSyntheticEvent`. We only populate `nativeEvent`
|
|
35
|
+
* since our consumers (mirroring upstream `react-native-pager-view`) read
|
|
36
|
+
* only `event.nativeEvent.X`; the unset SyntheticEvent fields would never
|
|
37
|
+
* be observed in practice.
|
|
38
|
+
*/
|
|
39
|
+
export const wrapNativeEvent = <T,>(nativeEvent: T): NativeSyntheticEvent<T> => {
|
|
40
|
+
'worklet';
|
|
41
|
+
return { nativeEvent } as NativeSyntheticEvent<T>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Props for the `PagerView` component.
|
|
46
|
+
* Compatible with `react-native-pager-view`.
|
|
47
|
+
*/
|
|
48
|
+
export type PagerViewProps = ViewProps & {
|
|
49
|
+
/**
|
|
50
|
+
* Ref handle exposing imperative `setPage`, `setPageWithoutAnimation`,
|
|
51
|
+
* and `setScrollEnabled` methods.
|
|
52
|
+
*/
|
|
53
|
+
ref?: Ref<PagerViewRef>;
|
|
54
|
+
/**
|
|
55
|
+
* Index of the page that is initially selected. Read **once** on mount;
|
|
56
|
+
* later changes are ignored. To navigate after mount, call
|
|
57
|
+
* `ref.setPage()` or `ref.setPageWithoutAnimation()`.
|
|
58
|
+
* @default 0
|
|
59
|
+
*/
|
|
60
|
+
initialPage?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the user can swipe between pages.
|
|
63
|
+
* @default true
|
|
64
|
+
*/
|
|
65
|
+
scrollEnabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Layout direction for paging.
|
|
68
|
+
* @default 'ltr'
|
|
69
|
+
* @platform android
|
|
70
|
+
*/
|
|
71
|
+
layoutDirection?: 'ltr' | 'rtl';
|
|
72
|
+
/**
|
|
73
|
+
* Number of pages kept off-screen on each side of the visible page.
|
|
74
|
+
* @platform android
|
|
75
|
+
*/
|
|
76
|
+
offscreenPageLimit?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Pixels of padding between pages.
|
|
79
|
+
* @platform android
|
|
80
|
+
*/
|
|
81
|
+
pageMargin?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Fires continuously while a swipe is in progress. The event's `position`
|
|
84
|
+
* is the index of the leading visible page; `offset` is the fractional
|
|
85
|
+
* progress toward the next page in the `[0, 1)` range.
|
|
86
|
+
*
|
|
87
|
+
* **iOS 18+ only.**
|
|
88
|
+
*/
|
|
89
|
+
onPageScroll?: (event: PagerViewOnPageScrollEvent) => void;
|
|
90
|
+
/**
|
|
91
|
+
* Fires when a page is fully selected. The event's `position` is the
|
|
92
|
+
* index of the new page.
|
|
93
|
+
*/
|
|
94
|
+
onPageSelected?: (event: PagerViewOnPageSelectedEvent) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Fires when the scroll state changes between `idle`, `dragging`,
|
|
97
|
+
* and `settling`.
|
|
98
|
+
*
|
|
99
|
+
* **iOS 18+ only.**
|
|
100
|
+
*/
|
|
101
|
+
onPageScrollStateChanged?: (event: PageScrollStateChangedEvent) => void;
|
|
102
|
+
/**
|
|
103
|
+
* Pages of the pager. Each child is treated as a separate page and
|
|
104
|
+
* stretched to fill the pager. Each child should have a stable `key`.
|
|
105
|
+
*/
|
|
106
|
+
children?: ReactNode;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Ref handle for the `PagerView` component.
|
|
111
|
+
* Compatible with `react-native-pager-view`.
|
|
112
|
+
*/
|
|
113
|
+
export type PagerViewRef = {
|
|
114
|
+
/**
|
|
115
|
+
* Animate the pager to the given page index. Out-of-range indices are
|
|
116
|
+
* silently ignored. On iOS the animation requires `react-native-worklets`;
|
|
117
|
+
* without it, `setPage` falls back to a non-animated jump.
|
|
118
|
+
*/
|
|
119
|
+
setPage: (selectedPage: number) => void;
|
|
120
|
+
/**
|
|
121
|
+
* Jump to the given page index without an animation.
|
|
122
|
+
*/
|
|
123
|
+
setPageWithoutAnimation: (selectedPage: number) => void;
|
|
124
|
+
/**
|
|
125
|
+
* Imperatively enable or disable user scrolling — convenient when
|
|
126
|
+
* toggling from a non-React context like a ref-based gesture handler.
|
|
127
|
+
*
|
|
128
|
+
* > **Note:** If the `scrollEnabled` prop is also provided, subsequent
|
|
129
|
+
* > prop changes win and reset the value set imperatively. To use the
|
|
130
|
+
* > imperative path exclusively, omit the prop.
|
|
131
|
+
*
|
|
132
|
+
* > Unlike upstream `react-native-pager-view` (which calls UIManager
|
|
133
|
+
* > directly), this triggers a re-render of `PagerView` so the new
|
|
134
|
+
* > flag flows through to the native view via props.
|
|
135
|
+
*/
|
|
136
|
+
setScrollEnabled: (scrollEnabled: boolean) => void;
|
|
137
|
+
};
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type PickerItemValue,
|
|
8
8
|
type PickerProps,
|
|
9
9
|
} from './types';
|
|
10
|
-
import { useNativeState } from '../../State
|
|
10
|
+
import { useNativeState } from '../../State';
|
|
11
11
|
import { DropdownMenuItem } from '../../jetpack-compose/DropdownMenu/DropdownMenuItem';
|
|
12
12
|
import {
|
|
13
13
|
ExposedDropdownMenuBox,
|
|
@@ -12,7 +12,8 @@ export function SegmentsSeparators({
|
|
|
12
12
|
values: number;
|
|
13
13
|
selectedIndex?: number;
|
|
14
14
|
}) {
|
|
15
|
-
const
|
|
15
|
+
const isDark = useColorScheme() === 'dark';
|
|
16
|
+
|
|
16
17
|
const hide = (val: number) => {
|
|
17
18
|
return selectedIndex === val || selectedIndex === val + 1;
|
|
18
19
|
};
|
|
@@ -22,11 +23,7 @@ export function SegmentsSeparators({
|
|
|
22
23
|
{[...Array(values - 1).keys()].map((val) => (
|
|
23
24
|
<View
|
|
24
25
|
key={val}
|
|
25
|
-
style={[
|
|
26
|
-
styles.separator,
|
|
27
|
-
colorScheme === 'dark' && styles.darkSeparator,
|
|
28
|
-
hide(val) && styles.hide,
|
|
29
|
-
]}
|
|
26
|
+
style={[styles.separator, isDark && styles.darkSeparator, hide(val) && styles.hide]}
|
|
30
27
|
/>
|
|
31
28
|
))}
|
|
32
29
|
</View>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { requireNativeView } from 'expo';
|
|
2
2
|
import type { Ref } from 'react';
|
|
3
3
|
|
|
4
|
+
import { getStateId, useWorkletProp, worklets } from '../../State';
|
|
4
5
|
import { type ModifierConfig, type ViewEvent } from '../../types';
|
|
5
6
|
import { type PaddingValuesRecord } from '../Carousel';
|
|
6
7
|
import { createViewModifierEventListener } from '../modifiers/utils';
|
|
@@ -19,6 +20,12 @@ export type HorizontalPagerHandle = {
|
|
|
19
20
|
scrollToPage: (page: number) => Promise<void>;
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Kind of drag interaction reported by `onDragInteraction`. Mirrors Compose's
|
|
25
|
+
* `DragInteraction.Start` / `DragInteraction.Stop` / `DragInteraction.Cancel`.
|
|
26
|
+
*/
|
|
27
|
+
export type HorizontalPagerDragInteraction = 'start' | 'stop' | 'cancel';
|
|
28
|
+
|
|
22
29
|
export type HorizontalPagerProps = {
|
|
23
30
|
/**
|
|
24
31
|
* Imperative handle for programmatic navigation. Mirrors the methods on
|
|
@@ -42,6 +49,28 @@ export type HorizontalPagerProps = {
|
|
|
42
49
|
* swipe or programmatic scroll has fully settled.
|
|
43
50
|
*/
|
|
44
51
|
onSettledPageChange?: (page: number) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Fires continuously while a swipe is in progress. Mirrors Compose's
|
|
54
|
+
* `PagerState.currentPage` and `currentPageOffsetFraction` — the latter is
|
|
55
|
+
* the signed fractional offset from `currentPage`, in the `[-0.5, 0.5]` range.
|
|
56
|
+
*
|
|
57
|
+
* If the callback is marked with the `'worklet'` directive, it runs
|
|
58
|
+
* synchronously on the UI thread; otherwise it is delivered asynchronously
|
|
59
|
+
* as a regular JS event.
|
|
60
|
+
*/
|
|
61
|
+
onPageScroll?: (currentPage: number, currentPageOffsetFraction: number) => void;
|
|
62
|
+
/**
|
|
63
|
+
* Fires when Compose's `PagerState.isScrollInProgress` toggles — true while
|
|
64
|
+
* the pager is being dragged or animating to a snap target, false once it
|
|
65
|
+
* has settled.
|
|
66
|
+
*/
|
|
67
|
+
onScrollInProgressChange?: (isScrollInProgress: boolean) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Fires for each drag interaction emitted by `PagerState.interactionSource`.
|
|
70
|
+
* Combine with `onScrollInProgressChange` to distinguish user dragging from
|
|
71
|
+
* fling/snap-settling.
|
|
72
|
+
*/
|
|
73
|
+
onDragInteraction?: (kind: HorizontalPagerDragInteraction) => void;
|
|
45
74
|
/**
|
|
46
75
|
* Spacing between pages in dp.
|
|
47
76
|
* @default 0
|
|
@@ -79,35 +108,77 @@ export type HorizontalPagerProps = {
|
|
|
79
108
|
|
|
80
109
|
type NativeHorizontalPagerProps = Omit<
|
|
81
110
|
HorizontalPagerProps,
|
|
82
|
-
|
|
111
|
+
| 'onCurrentPageChange'
|
|
112
|
+
| 'onSettledPageChange'
|
|
113
|
+
| 'onPageScroll'
|
|
114
|
+
| 'onScrollInProgressChange'
|
|
115
|
+
| 'onDragInteraction'
|
|
83
116
|
> &
|
|
84
117
|
ViewEvent<'onCurrentPageChange', { position: number }> &
|
|
85
|
-
ViewEvent<'onSettledPageChange', { position: number }
|
|
118
|
+
ViewEvent<'onSettledPageChange', { position: number }> &
|
|
119
|
+
ViewEvent<'onPageScroll', { currentPage: number; currentPageOffsetFraction: number }> &
|
|
120
|
+
ViewEvent<'onScrollInProgressChange', { isScrollInProgress: boolean }> &
|
|
121
|
+
ViewEvent<'onDragInteraction', { kind: HorizontalPagerDragInteraction }> & {
|
|
122
|
+
onPageScrollSync?: number | null;
|
|
123
|
+
};
|
|
86
124
|
|
|
87
125
|
const NativeView: React.ComponentType<NativeHorizontalPagerProps> = requireNativeView(
|
|
88
126
|
'ExpoUI',
|
|
89
127
|
'HorizontalPagerView'
|
|
90
128
|
);
|
|
91
129
|
|
|
92
|
-
function transformProps(props: HorizontalPagerProps): NativeHorizontalPagerProps {
|
|
93
|
-
const { modifiers, onCurrentPageChange, onSettledPageChange, ...restProps } = props;
|
|
94
|
-
return {
|
|
95
|
-
modifiers,
|
|
96
|
-
...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
|
|
97
|
-
...restProps,
|
|
98
|
-
onCurrentPageChange: onCurrentPageChange
|
|
99
|
-
? ({ nativeEvent: { position } }) => onCurrentPageChange(position)
|
|
100
|
-
: undefined,
|
|
101
|
-
onSettledPageChange: onSettledPageChange
|
|
102
|
-
? ({ nativeEvent: { position } }) => onSettledPageChange(position)
|
|
103
|
-
: undefined,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
130
|
/**
|
|
108
131
|
* A horizontally scrolling pager that snaps to individual pages,
|
|
109
132
|
* matching Compose's `HorizontalPager`.
|
|
110
133
|
*/
|
|
111
134
|
export function HorizontalPager(props: HorizontalPagerProps) {
|
|
112
|
-
|
|
135
|
+
const {
|
|
136
|
+
modifiers,
|
|
137
|
+
onCurrentPageChange,
|
|
138
|
+
onSettledPageChange,
|
|
139
|
+
onPageScroll,
|
|
140
|
+
onScrollInProgressChange,
|
|
141
|
+
onDragInteraction,
|
|
142
|
+
...restProps
|
|
143
|
+
} = props;
|
|
144
|
+
|
|
145
|
+
const isPageScrollWorklet = !!onPageScroll && !!worklets?.isWorkletFunction?.(onPageScroll);
|
|
146
|
+
const pageScrollWorkletCallback = useWorkletProp(
|
|
147
|
+
isPageScrollWorklet ? onPageScroll : undefined,
|
|
148
|
+
'onPageScroll'
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<NativeView
|
|
153
|
+
{...restProps}
|
|
154
|
+
modifiers={modifiers}
|
|
155
|
+
{...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
|
|
156
|
+
onCurrentPageChange={
|
|
157
|
+
onCurrentPageChange
|
|
158
|
+
? ({ nativeEvent: { position } }) => onCurrentPageChange(position)
|
|
159
|
+
: undefined
|
|
160
|
+
}
|
|
161
|
+
onSettledPageChange={
|
|
162
|
+
onSettledPageChange
|
|
163
|
+
? ({ nativeEvent: { position } }) => onSettledPageChange(position)
|
|
164
|
+
: undefined
|
|
165
|
+
}
|
|
166
|
+
onPageScroll={
|
|
167
|
+
!isPageScrollWorklet && onPageScroll
|
|
168
|
+
? ({ nativeEvent: { currentPage, currentPageOffsetFraction } }) =>
|
|
169
|
+
onPageScroll(currentPage, currentPageOffsetFraction)
|
|
170
|
+
: undefined
|
|
171
|
+
}
|
|
172
|
+
onPageScrollSync={getStateId(pageScrollWorkletCallback)}
|
|
173
|
+
onScrollInProgressChange={
|
|
174
|
+
onScrollInProgressChange
|
|
175
|
+
? ({ nativeEvent: { isScrollInProgress } }) =>
|
|
176
|
+
onScrollInProgressChange(isScrollInProgress)
|
|
177
|
+
: undefined
|
|
178
|
+
}
|
|
179
|
+
onDragInteraction={
|
|
180
|
+
onDragInteraction ? ({ nativeEvent: { kind } }) => onDragInteraction(kind) : undefined
|
|
181
|
+
}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
113
184
|
}
|