@lodev09/react-native-true-sheet 4.0.0-beta.0 → 4.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +42 -31
- package/RNTrueSheet.podspec +10 -2
- package/android/build.gradle +2 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +174 -49
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerViewManager.kt +7 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +371 -18
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +14 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +150 -21
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterViewManager.kt +14 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderView.kt +38 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderViewManager.kt +28 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +84 -24
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +5 -4
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPeekView.kt +72 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPeekViewManager.kt +33 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +45 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +526 -231
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +1217 -603
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +136 -29
- package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +63 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +36 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +255 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +303 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +277 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +162 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +180 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetKeyboardObserver.kt +207 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +220 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetDragEvents.kt +71 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetFocusEvents.kt +65 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +112 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetStateEvents.kt +56 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +84 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +93 -67
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +30 -0
- package/android/src/main/jni/CMakeLists.txt +63 -0
- package/android/src/main/jni/TrueSheetSpec.h +19 -0
- package/android/src/main/jni/TrueSheetStateUpdater.cpp +96 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +24 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +43 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +28 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +11 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +37 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +24 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +56 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +28 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +11 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +41 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +18 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +16 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +28 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +61 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +45 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +23 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +52 -0
- package/ios/TrueSheetContainerView.h +81 -6
- package/ios/TrueSheetContainerView.mm +275 -21
- package/ios/TrueSheetContentView.h +38 -1
- package/ios/TrueSheetContentView.mm +413 -43
- package/ios/TrueSheetFooterView.h +27 -2
- package/ios/TrueSheetFooterView.mm +141 -35
- package/ios/{events/OnDidDismissEvent.h → TrueSheetHeaderView.h} +10 -7
- package/ios/TrueSheetHeaderView.mm +64 -0
- package/ios/TrueSheetModule.mm +92 -5
- package/ios/TrueSheetPeekView.h +32 -0
- package/ios/TrueSheetPeekView.mm +99 -0
- package/ios/TrueSheetView.h +7 -15
- package/ios/TrueSheetView.mm +633 -215
- package/ios/TrueSheetViewController.h +84 -16
- package/ios/TrueSheetViewController.mm +1136 -333
- package/ios/core/RNScreensEventObserver.h +46 -0
- package/ios/core/RNScreensEventObserver.mm +280 -0
- package/ios/core/TrueSheetBlurView.h +27 -0
- package/ios/core/TrueSheetBlurView.mm +82 -0
- package/ios/core/TrueSheetDetentCalculator.h +107 -0
- package/ios/core/TrueSheetDetentCalculator.mm +197 -0
- package/ios/core/TrueSheetGrabberView.h +72 -0
- package/ios/core/TrueSheetGrabberView.mm +178 -0
- package/ios/core/TrueSheetKeyboardObserver.h +39 -0
- package/ios/core/TrueSheetKeyboardObserver.mm +112 -0
- package/ios/events/TrueSheetDragEvents.h +39 -0
- package/ios/events/TrueSheetDragEvents.mm +62 -0
- package/ios/events/{OnPositionChangeEvent.h → TrueSheetFocusEvents.h} +8 -5
- package/ios/events/TrueSheetFocusEvents.mm +49 -0
- package/ios/events/TrueSheetLifecycleEvents.h +40 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +71 -0
- package/ios/events/TrueSheetStateEvents.h +35 -0
- package/ios/events/TrueSheetStateEvents.mm +49 -0
- package/ios/tvos/TrueSheetStubs.mm +154 -0
- package/ios/{events/OnMountEvent.h → utils/BlurUtil.h} +5 -7
- package/ios/utils/BlurUtil.mm +69 -0
- package/ios/utils/GestureUtil.h +7 -0
- package/ios/utils/GestureUtil.mm +12 -0
- package/ios/utils/PlatformUtil.h +15 -0
- package/ios/utils/UIView+FirstResponder.h +15 -0
- package/ios/utils/UIView+FirstResponder.mm +26 -0
- package/ios/{events/OnWillDismissEvent.h → utils/UIView+ScrollEdgeInteraction.h} +6 -7
- package/ios/utils/UIView+ScrollEdgeInteraction.mm +62 -0
- package/ios/utils/WindowUtil.mm +17 -1
- package/lib/module/TrueSheet.js +224 -64
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +1201 -0
- package/lib/module/TrueSheet.web.js.map +1 -0
- package/lib/module/TrueSheetPeek.js +15 -0
- package/lib/module/TrueSheetPeek.js.map +1 -0
- package/lib/module/TrueSheetPeek.web.js +55 -0
- package/lib/module/TrueSheetPeek.web.js.map +1 -0
- package/lib/module/TrueSheetProvider.js +28 -0
- package/lib/module/TrueSheetProvider.js.map +1 -0
- package/lib/module/TrueSheetProvider.web.js +157 -0
- package/lib/module/TrueSheetProvider.web.js.map +1 -0
- package/lib/module/fabric/TrueSheetContainerViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
- package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
- package/lib/module/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
- package/lib/module/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +105 -18
- package/lib/module/index.js +3 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +93 -0
- package/lib/module/mocks/index.js.map +1 -0
- package/lib/module/mocks/navigation.js +85 -0
- package/lib/module/mocks/navigation.js.map +1 -0
- package/lib/module/mocks/reanimated.js +91 -0
- package/lib/module/mocks/reanimated.js.map +1 -0
- package/lib/module/navigation/TrueSheetRouter.js +170 -0
- package/lib/module/navigation/TrueSheetRouter.js.map +1 -0
- package/lib/module/navigation/TrueSheetView.js +71 -0
- package/lib/module/navigation/TrueSheetView.js.map +1 -0
- package/lib/module/navigation/createTrueSheetNavigator.js +63 -0
- package/lib/module/navigation/createTrueSheetNavigator.js.map +1 -0
- package/lib/module/navigation/index.js +6 -0
- package/lib/module/navigation/index.js.map +1 -0
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +56 -0
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -0
- package/lib/module/navigation/screen/TrueSheetScreen.js +49 -0
- package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -0
- package/lib/module/navigation/screen/index.js +5 -0
- package/lib/module/navigation/screen/index.js.map +1 -0
- package/lib/module/navigation/screen/types.js +4 -0
- package/lib/module/navigation/screen/types.js.map +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js +68 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -0
- package/lib/module/navigation/types.js +4 -0
- package/lib/module/navigation/types.js.map +1 -0
- package/lib/module/navigation/useTrueSheetNavigation.js +24 -0
- package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -0
- package/lib/module/reanimated/ReanimatedTrueSheet.js +15 -9
- package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -1
- package/lib/module/reanimated/ReanimatedTrueSheet.web.js +81 -0
- package/lib/module/reanimated/ReanimatedTrueSheet.web.js.map +1 -0
- package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +4 -2
- package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
- package/lib/module/reanimated/index.js +2 -2
- package/lib/module/reanimated/index.js.map +1 -1
- package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js +20 -0
- package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js.map +1 -0
- package/lib/module/specs/NativeTrueSheetModule.js.map +1 -1
- package/lib/module/web/constants.js +16 -0
- package/lib/module/web/constants.js.map +1 -0
- package/lib/module/web/vaul/README.md +9 -0
- package/lib/module/web/vaul/browser.js +30 -0
- package/lib/module/web/vaul/browser.js.map +1 -0
- package/lib/module/web/vaul/constants.js +15 -0
- package/lib/module/web/vaul/constants.js.map +1 -0
- package/lib/module/web/vaul/context.js +59 -0
- package/lib/module/web/vaul/context.js.map +1 -0
- package/lib/module/web/vaul/helpers.js +93 -0
- package/lib/module/web/vaul/helpers.js.map +1 -0
- package/lib/module/web/vaul/index.js +1309 -0
- package/lib/module/web/vaul/index.js.map +1 -0
- package/lib/module/web/vaul/style.css +287 -0
- package/lib/module/web/vaul/style.css.d.js +2 -0
- package/lib/module/web/vaul/style.css.d.js.map +1 -0
- package/lib/module/web/vaul/types.js +2 -0
- package/lib/module/web/vaul/types.js.map +1 -0
- package/lib/module/web/vaul/use-composed-refs.js +34 -0
- package/lib/module/web/vaul/use-composed-refs.js.map +1 -0
- package/lib/module/web/vaul/use-controllable-state.js +54 -0
- package/lib/module/web/vaul/use-controllable-state.js.map +1 -0
- package/lib/module/web/vaul/use-position-fixed.js +123 -0
- package/lib/module/web/vaul/use-position-fixed.js.map +1 -0
- package/lib/module/web/vaul/use-prevent-scroll.js +258 -0
- package/lib/module/web/vaul/use-prevent-scroll.js.map +1 -0
- package/lib/module/web/vaul/use-scale-background.js +57 -0
- package/lib/module/web/vaul/use-scale-background.js.map +1 -0
- package/lib/module/web/vaul/use-snap-points.js +305 -0
- package/lib/module/web/vaul/use-snap-points.js.map +1 -0
- package/lib/typescript/src/TrueSheet.d.ts +51 -14
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +463 -54
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts +5 -0
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.d.ts +9 -0
- package/lib/typescript/src/TrueSheetPeek.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts +28 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetProvider.d.ts +18 -0
- package/lib/typescript/src/TrueSheetProvider.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetProvider.web.d.ts +27 -0
- package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -0
- package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +67 -16
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +43 -0
- package/lib/typescript/src/mocks/index.d.ts.map +1 -0
- package/lib/typescript/src/mocks/navigation.d.ts +66 -0
- package/lib/typescript/src/mocks/navigation.d.ts.map +1 -0
- package/lib/typescript/src/mocks/reanimated.d.ts +48 -0
- package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -0
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +57 -0
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +1 -0
- package/lib/typescript/src/navigation/TrueSheetView.d.ts +10 -0
- package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +35 -0
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +6 -0
- package/lib/typescript/src/navigation/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +3 -0
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +3 -0
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/index.d.ts +4 -0
- package/lib/typescript/src/navigation/screen/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/types.d.ts +15 -0
- package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +35 -0
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -0
- package/lib/typescript/src/navigation/types.d.ts +153 -0
- package/lib/typescript/src/navigation/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +23 -0
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -0
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts +41 -0
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts.map +1 -0
- package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +8 -2
- package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts +15 -0
- package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts +25 -3
- package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts.map +1 -1
- package/lib/typescript/src/web/constants.d.ts +14 -0
- package/lib/typescript/src/web/constants.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/browser.d.ts +8 -0
- package/lib/typescript/src/web/vaul/browser.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +13 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts +53 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts +17 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/index.d.ts +228 -0
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/types.d.ts +7 -0
- package/lib/typescript/src/web/vaul/types.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-composed-refs.d.ts +14 -0
- package/lib/typescript/src/web/vaul/use-composed-refs.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-controllable-state.d.ts +9 -0
- package/lib/typescript/src/web/vaul/use-controllable-state.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-position-fixed.d.ts +19 -0
- package/lib/typescript/src/web/vaul/use-position-fixed.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts +18 -0
- package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-scale-background.d.ts +2 -0
- package/lib/typescript/src/web/vaul/use-scale-background.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts +38 -0
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -0
- package/package.json +78 -34
- package/react-native.config.js +9 -2
- package/src/TrueSheet.tsx +276 -60
- package/src/TrueSheet.types.ts +525 -71
- package/src/TrueSheet.web.tsx +1366 -0
- package/src/TrueSheetPeek.tsx +11 -0
- package/src/TrueSheetPeek.web.tsx +63 -0
- package/src/TrueSheetProvider.tsx +33 -0
- package/src/TrueSheetProvider.web.tsx +220 -0
- package/src/fabric/TrueSheetContainerViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
- package/src/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
- package/src/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
- package/src/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +105 -18
- package/src/index.ts +2 -2
- package/src/mocks/index.ts +89 -0
- package/src/mocks/navigation.ts +72 -0
- package/src/mocks/reanimated.ts +101 -0
- package/src/navigation/TrueSheetRouter.ts +234 -0
- package/src/navigation/TrueSheetView.tsx +99 -0
- package/src/navigation/createTrueSheetNavigator.tsx +93 -0
- package/src/navigation/index.ts +14 -0
- package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +61 -0
- package/src/navigation/screen/TrueSheetScreen.tsx +54 -0
- package/src/navigation/screen/index.ts +3 -0
- package/src/navigation/screen/types.ts +21 -0
- package/src/navigation/screen/useSheetScreenState.ts +107 -0
- package/src/navigation/types.ts +219 -0
- package/src/navigation/useTrueSheetNavigation.ts +26 -0
- package/src/reanimated/ReanimatedTrueSheet.tsx +12 -7
- package/src/reanimated/ReanimatedTrueSheet.web.tsx +75 -0
- package/src/reanimated/ReanimatedTrueSheetProvider.tsx +12 -8
- package/src/reanimated/useReanimatedPositionChangeHandler.web.ts +31 -0
- package/src/specs/NativeTrueSheetModule.ts +28 -3
- package/src/web/constants.ts +13 -0
- package/src/web/vaul/README.md +9 -0
- package/src/web/vaul/browser.ts +38 -0
- package/src/web/vaul/constants.ts +20 -0
- package/src/web/vaul/context.ts +101 -0
- package/src/web/vaul/helpers.ts +124 -0
- package/src/web/vaul/index.tsx +1740 -0
- package/src/web/vaul/style.css +287 -0
- package/src/web/vaul/style.css.d.ts +1 -0
- package/src/web/vaul/types.ts +7 -0
- package/src/web/vaul/use-composed-refs.ts +35 -0
- package/src/web/vaul/use-controllable-state.ts +66 -0
- package/src/web/vaul/use-position-fixed.ts +147 -0
- package/src/web/vaul/use-prevent-scroll.ts +309 -0
- package/src/web/vaul/use-scale-background.ts +64 -0
- package/src/web/vaul/use-snap-points.ts +455 -0
- package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +0 -20
- package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +0 -20
- package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +0 -32
- package/android/src/main/java/com/lodev09/truesheet/events/SizeChangeEvent.kt +0 -27
- package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +0 -20
- package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +0 -26
- package/android/src/main/res/values/styles.xml +0 -8
- package/ios/events/OnDetentChangeEvent.h +0 -28
- package/ios/events/OnDetentChangeEvent.mm +0 -30
- package/ios/events/OnDidDismissEvent.mm +0 -25
- package/ios/events/OnDidPresentEvent.h +0 -28
- package/ios/events/OnDidPresentEvent.mm +0 -30
- package/ios/events/OnDragBeginEvent.h +0 -28
- package/ios/events/OnDragBeginEvent.mm +0 -30
- package/ios/events/OnDragChangeEvent.h +0 -28
- package/ios/events/OnDragChangeEvent.mm +0 -30
- package/ios/events/OnDragEndEvent.h +0 -28
- package/ios/events/OnDragEndEvent.mm +0 -30
- package/ios/events/OnMountEvent.mm +0 -25
- package/ios/events/OnPositionChangeEvent.mm +0 -32
- package/ios/events/OnSizeChangeEvent.h +0 -28
- package/ios/events/OnSizeChangeEvent.mm +0 -30
- package/ios/events/OnWillDismissEvent.mm +0 -25
- package/ios/events/OnWillPresentEvent.h +0 -28
- package/ios/events/OnWillPresentEvent.mm +0 -30
- package/ios/utils/LayoutUtil.h +0 -44
- package/ios/utils/LayoutUtil.mm +0 -50
- package/lib/module/TrueSheetGrabber.js +0 -51
- package/lib/module/TrueSheetGrabber.js.map +0 -1
- package/lib/typescript/src/TrueSheetGrabber.d.ts +0 -39
- package/lib/typescript/src/TrueSheetGrabber.d.ts.map +0 -1
- package/src/TrueSheetGrabber.tsx +0 -82
- package/src/__mocks__/index.js +0 -67
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.res.Configuration
|
|
6
|
+
import android.view.MotionEvent
|
|
7
|
+
import android.view.View
|
|
8
|
+
import android.view.ViewConfiguration
|
|
9
|
+
import android.view.ViewGroup
|
|
10
|
+
import android.widget.EditText
|
|
11
|
+
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
12
|
+
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
13
|
+
import com.facebook.react.uimanager.PointerEvents
|
|
14
|
+
import com.facebook.react.uimanager.ReactPointerEventsView
|
|
15
|
+
import com.facebook.react.uimanager.TouchTargetHelper
|
|
16
|
+
import com.lodev09.truesheet.utils.isDescendantOf
|
|
17
|
+
|
|
18
|
+
interface TrueSheetCoordinatorLayoutDelegate {
|
|
19
|
+
fun coordinatorLayoutDidLayout(changed: Boolean)
|
|
20
|
+
fun coordinatorLayoutDidChangeConfiguration()
|
|
21
|
+
fun findScrollView(): ViewGroup?
|
|
22
|
+
fun findSheetView(): TrueSheetBottomSheetView?
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Custom CoordinatorLayout that hosts the bottom sheet and dim view.
|
|
27
|
+
* Implements ReactPointerEventsView to allow touch events to pass through
|
|
28
|
+
* to underlying React Native views when appropriate.
|
|
29
|
+
*/
|
|
30
|
+
@SuppressLint("ViewConstructor")
|
|
31
|
+
class TrueSheetCoordinatorLayout(context: Context) :
|
|
32
|
+
CoordinatorLayout(context),
|
|
33
|
+
ReactPointerEventsView {
|
|
34
|
+
|
|
35
|
+
var delegate: TrueSheetCoordinatorLayoutDelegate? = null
|
|
36
|
+
|
|
37
|
+
private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
|
|
38
|
+
private var dragging = false
|
|
39
|
+
private var initialY = 0f
|
|
40
|
+
private var activePointerId = 0
|
|
41
|
+
|
|
42
|
+
// Horizontal-dominance tracking. iOS lets horizontal child gestures win over
|
|
43
|
+
// the sheet's vertical pan; we mirror that by locking out BottomSheetBehavior
|
|
44
|
+
// interception once the first significant movement of a stream is horizontal.
|
|
45
|
+
private var streamInitialX = 0f
|
|
46
|
+
private var streamInitialY = 0f
|
|
47
|
+
private var streamHorizontalLocked = false
|
|
48
|
+
private var streamDirectionDecided = false
|
|
49
|
+
|
|
50
|
+
// Native-gesture-claim tracking. RNGH handlers activate past BottomSheetBehavior's
|
|
51
|
+
// drag slop and never call requestDisallowInterceptTouchEvent, so the sheet would
|
|
52
|
+
// steal vertical gestures (e.g. a vertical pan) before they can activate. While the
|
|
53
|
+
// touch is inside a descendant GestureHandlerRootView we hold off interception within
|
|
54
|
+
// a grace distance; once a child claims the gesture (relayed from the RootView impls
|
|
55
|
+
// via childDidClaimNativeGesture) we yield the rest of the stream, mirroring iOS.
|
|
56
|
+
private var streamHasGestureRoot = false
|
|
57
|
+
private var streamGestureClaimed = false
|
|
58
|
+
|
|
59
|
+
// Whether the sheet owns vertical drags for this stream: the touch target is inside
|
|
60
|
+
// the sheet but outside the pinned scrollable (e.g. a header overlaying it), with
|
|
61
|
+
// nothing vertically scrollable along its chain. Such targets consume the stream
|
|
62
|
+
// natively (no nested scroll), and BottomSheetBehavior won't intercept because the
|
|
63
|
+
// scrollable's bounds still contain the point (touchingScrollingChild) — intercept
|
|
64
|
+
// manually.
|
|
65
|
+
private var streamSheetDraggable = false
|
|
66
|
+
private var streamDraggableEditText = false
|
|
67
|
+
|
|
68
|
+
init {
|
|
69
|
+
layoutParams = LayoutParams(
|
|
70
|
+
LayoutParams.MATCH_PARENT,
|
|
71
|
+
LayoutParams.MATCH_PARENT
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
clipChildren = false
|
|
75
|
+
clipToPadding = false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override fun onLayout(
|
|
79
|
+
changed: Boolean,
|
|
80
|
+
l: Int,
|
|
81
|
+
t: Int,
|
|
82
|
+
r: Int,
|
|
83
|
+
b: Int
|
|
84
|
+
) {
|
|
85
|
+
super.onLayout(changed, l, t, r, b)
|
|
86
|
+
delegate?.coordinatorLayoutDidLayout(changed)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override fun onConfigurationChanged(newConfig: Configuration?) {
|
|
90
|
+
super.onConfigurationChanged(newConfig)
|
|
91
|
+
delegate?.coordinatorLayoutDidChangeConfiguration()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override val pointerEvents: PointerEvents
|
|
95
|
+
get() = PointerEvents.BOX_NONE
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Clears stale `nestedScrollingChildRef` from BottomSheetBehavior.
|
|
99
|
+
*
|
|
100
|
+
* The cached `nestedScrollingChildRef` can become stale when a child sheet
|
|
101
|
+
* with a ScrollView is dismissed and its ScrollView returns to this hierarchy.
|
|
102
|
+
*/
|
|
103
|
+
private fun clearStaleNestedScrollingChildRef() {
|
|
104
|
+
val sheet = delegate?.findSheetView() ?: return
|
|
105
|
+
val behavior = sheet.behavior ?: return
|
|
106
|
+
try {
|
|
107
|
+
val field = behavior.javaClass.superclass.getDeclaredField("nestedScrollingChildRef")
|
|
108
|
+
field.isAccessible = true
|
|
109
|
+
@Suppress("UNCHECKED_CAST")
|
|
110
|
+
val ref = field.get(behavior) as? java.lang.ref.WeakReference<android.view.View> ?: return
|
|
111
|
+
val view = ref.get()
|
|
112
|
+
if (view == null || !view.isDescendantOf(sheet)) {
|
|
113
|
+
ref.clear()
|
|
114
|
+
}
|
|
115
|
+
} catch (_: Exception) {}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
119
|
+
// RN's ReactEditText fires this(true) on ACTION_DOWN, killing sheet drag over an input.
|
|
120
|
+
// Swallow only that case — nothing between the touched EditText and the sheet scrolls
|
|
121
|
+
// (an overflowing multiline EditText or an input inside a scrollable that can actually
|
|
122
|
+
// scroll still scrolls). Other children (maps, sliders, scrollables) keep the standard
|
|
123
|
+
// disallow contract.
|
|
124
|
+
if (disallowIntercept && streamDraggableEditText) return
|
|
125
|
+
super.requestDisallowInterceptTouchEvent(disallowIntercept)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolves the stream's touch target (respecting zIndex and pointerEvents) and
|
|
130
|
+
* decides whether the sheet may drag from it — inside the sheet, outside the pinned
|
|
131
|
+
* scrollable, and nothing along the target's chain scrolls vertically.
|
|
132
|
+
*/
|
|
133
|
+
private fun updateStreamTargetFlags(ev: MotionEvent) {
|
|
134
|
+
streamSheetDraggable = false
|
|
135
|
+
streamDraggableEditText = false
|
|
136
|
+
|
|
137
|
+
val sheet = delegate?.findSheetView() ?: return
|
|
138
|
+
val target = TouchTargetHelper.findTargetPathAndCoordinatesForTouch(ev.x, ev.y, this, FloatArray(2))
|
|
139
|
+
.firstNotNullOfOrNull { it.getView() } ?: return
|
|
140
|
+
if (target !== sheet && !target.isDescendantOf(sheet)) return
|
|
141
|
+
|
|
142
|
+
// A pinned scrollable that can scroll (or hosts pull-to-refresh) owns its
|
|
143
|
+
// touches — nested scrolling drags the sheet. When it can't scroll (e.g.
|
|
144
|
+
// the sheet is fully expanded and the content fits) it eats the gesture
|
|
145
|
+
// instead, so fall through and let the sheet drag from it — an EditText
|
|
146
|
+
// target needs the disallow-intercept swallow or its stream dies.
|
|
147
|
+
val scrollView = delegate?.findScrollView()
|
|
148
|
+
if (scrollView != null && (target === scrollView || target.isDescendantOf(scrollView))) {
|
|
149
|
+
val hasRefreshControl = scrollView.parent is SwipeRefreshLayout
|
|
150
|
+
if (hasRefreshControl || scrollView.scrollY > 0 || scrollView.canScrollVertically(1)) return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var view: View? = target
|
|
154
|
+
while (view != null && view !== this) {
|
|
155
|
+
if (view.canScrollVertically(1) || view.canScrollVertically(-1)) return
|
|
156
|
+
view = view.parent as? View
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
streamSheetDraggable = true
|
|
160
|
+
streamDraggableEditText = target is EditText
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private fun findDescendantViewAt(view: View, rawX: Int, rawY: Int, predicate: (View) -> Boolean): View? {
|
|
164
|
+
val loc = IntArray(2)
|
|
165
|
+
view.getLocationOnScreen(loc)
|
|
166
|
+
if (rawX < loc[0] || rawX > loc[0] + view.width || rawY < loc[1] || rawY > loc[1] + view.height) return null
|
|
167
|
+
if (predicate(view)) return view
|
|
168
|
+
if (view is ViewGroup) {
|
|
169
|
+
for (i in 0 until view.childCount) {
|
|
170
|
+
findDescendantViewAt(view.getChildAt(i), rawX, rawY, predicate)?.let { return it }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Called by descendant RootView implementations (controller, footer) when a child
|
|
178
|
+
* starts a native gesture — e.g. an RNGH handler activated or a scrollable began
|
|
179
|
+
* scrolling. Yields sheet interception for the rest of the touch stream.
|
|
180
|
+
*/
|
|
181
|
+
fun childDidClaimNativeGesture() {
|
|
182
|
+
streamGestureClaimed = true
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Intercepts touch events for ScrollViews that can't scroll (content < viewport),
|
|
187
|
+
* allowing the sheet to be dragged in these cases.
|
|
188
|
+
*
|
|
189
|
+
* TODO: Remove this workaround once NestedScrollView is merged into react-native core.
|
|
190
|
+
* See: https://github.com/facebook/react-native/pull/44099
|
|
191
|
+
*/
|
|
192
|
+
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
|
193
|
+
val action = ev.actionMasked
|
|
194
|
+
|
|
195
|
+
if (action == MotionEvent.ACTION_DOWN) {
|
|
196
|
+
clearStaleNestedScrollingChildRef()
|
|
197
|
+
|
|
198
|
+
streamInitialX = ev.rawX
|
|
199
|
+
streamInitialY = ev.rawY
|
|
200
|
+
streamHorizontalLocked = false
|
|
201
|
+
streamDirectionDecided = false
|
|
202
|
+
streamGestureClaimed = false
|
|
203
|
+
streamHasGestureRoot = findDescendantViewAt(this, ev.rawX.toInt(), ev.rawY.toInt()) {
|
|
204
|
+
it.javaClass.name == GESTURE_HANDLER_ROOT_VIEW_CLASS
|
|
205
|
+
} != null
|
|
206
|
+
updateStreamTargetFlags(ev)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Decide gesture direction on first significant movement of the stream.
|
|
210
|
+
// If horizontal wins, lock out BottomSheetBehavior for the rest of the stream
|
|
211
|
+
// so child handlers (carousels, swipe buttons, etc.) can claim the touch.
|
|
212
|
+
if (!streamDirectionDecided && action == MotionEvent.ACTION_MOVE) {
|
|
213
|
+
val dx = kotlin.math.abs(ev.rawX - streamInitialX)
|
|
214
|
+
val dy = kotlin.math.abs(ev.rawY - streamInitialY)
|
|
215
|
+
if (dx > touchSlop || dy > touchSlop) {
|
|
216
|
+
streamDirectionDecided = true
|
|
217
|
+
streamHorizontalLocked = dx > dy
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (streamHorizontalLocked) {
|
|
222
|
+
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
223
|
+
streamHorizontalLocked = false
|
|
224
|
+
streamDirectionDecided = false
|
|
225
|
+
}
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (streamGestureClaimed) {
|
|
230
|
+
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
231
|
+
streamGestureClaimed = false
|
|
232
|
+
}
|
|
233
|
+
return false
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Hold off BottomSheetBehavior within the grace distance so a gesture handler
|
|
237
|
+
// under the touch can activate and claim the stream first.
|
|
238
|
+
if (streamHasGestureRoot &&
|
|
239
|
+
action == MotionEvent.ACTION_MOVE &&
|
|
240
|
+
kotlin.math.abs(ev.rawY - streamInitialY) < touchSlop * GESTURE_CLAIM_SLOP_FACTOR
|
|
241
|
+
) {
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
val scrollView = delegate?.findScrollView()
|
|
246
|
+
val hasRefreshControl = scrollView?.parent is SwipeRefreshLayout
|
|
247
|
+
val cannotScroll = scrollView != null &&
|
|
248
|
+
!hasRefreshControl &&
|
|
249
|
+
scrollView.scrollY == 0 &&
|
|
250
|
+
!scrollView.canScrollVertically(1)
|
|
251
|
+
|
|
252
|
+
if (cannotScroll || streamSheetDraggable) {
|
|
253
|
+
when (ev.action and MotionEvent.ACTION_MASK) {
|
|
254
|
+
MotionEvent.ACTION_DOWN -> {
|
|
255
|
+
dragging = false
|
|
256
|
+
initialY = ev.y
|
|
257
|
+
activePointerId = ev.getPointerId(0)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
MotionEvent.ACTION_MOVE -> {
|
|
261
|
+
val pointerIndex = ev.findPointerIndex(activePointerId)
|
|
262
|
+
if (pointerIndex != -1) {
|
|
263
|
+
val y = ev.getY(pointerIndex)
|
|
264
|
+
val deltaY = initialY - y
|
|
265
|
+
if (kotlin.math.abs(deltaY) > touchSlop) {
|
|
266
|
+
dragging = true
|
|
267
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
MotionEvent.ACTION_UP,
|
|
273
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
274
|
+
dragging = false
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
dragging = false
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return dragging || super.onInterceptTouchEvent(ev)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
285
|
+
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
286
|
+
if (dragging) {
|
|
287
|
+
when (ev.action and MotionEvent.ACTION_MASK) {
|
|
288
|
+
MotionEvent.ACTION_UP,
|
|
289
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
290
|
+
dragging = false
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Let parent CoordinatorLayout handle the touch for BottomSheetBehavior
|
|
294
|
+
return super.onTouchEvent(ev)
|
|
295
|
+
}
|
|
296
|
+
return super.onTouchEvent(ev)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
companion object {
|
|
300
|
+
private const val GESTURE_HANDLER_ROOT_VIEW_CLASS = "com.swmansion.gesturehandler.react.RNGestureHandlerRootView"
|
|
301
|
+
private const val GESTURE_CLAIM_SLOP_FACTOR = 3
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
4
|
+
import com.facebook.react.uimanager.PixelUtil.pxToDp
|
|
5
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
|
+
import com.facebook.react.util.RNLog
|
|
7
|
+
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Provides screen dimensions and content measurements for detent calculations.
|
|
11
|
+
*/
|
|
12
|
+
interface TrueSheetDetentCalculatorDelegate {
|
|
13
|
+
val screenHeight: Int
|
|
14
|
+
val realScreenHeight: Int
|
|
15
|
+
val detents: MutableList<Double>
|
|
16
|
+
val contentHeight: Int
|
|
17
|
+
val headerHeight: Int
|
|
18
|
+
val absoluteHeader: Boolean
|
|
19
|
+
val footerHeight: Int
|
|
20
|
+
val absoluteFooter: Boolean
|
|
21
|
+
val peekContentHeight: Int
|
|
22
|
+
val contentBottomInset: Int
|
|
23
|
+
val maxContentHeight: Int?
|
|
24
|
+
val keyboardInset: Int
|
|
25
|
+
val topInset: Int
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Handles all detent-related calculations for the bottom sheet.
|
|
30
|
+
*/
|
|
31
|
+
class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
32
|
+
|
|
33
|
+
var delegate: TrueSheetDetentCalculatorDelegate? = null
|
|
34
|
+
|
|
35
|
+
private val screenHeight: Int get() = delegate?.screenHeight ?: 0
|
|
36
|
+
private val realScreenHeight: Int get() = delegate?.realScreenHeight ?: 0
|
|
37
|
+
private val detents: List<Double> get() = delegate?.detents ?: emptyList()
|
|
38
|
+
private val contentHeight: Int get() = delegate?.contentHeight ?: 0
|
|
39
|
+
|
|
40
|
+
private val headerHeight: Int get() = delegate?.headerHeight ?: 0
|
|
41
|
+
private val footerHeight: Int get() = delegate?.footerHeight ?: 0
|
|
42
|
+
private val peekContentHeight: Int get() = delegate?.peekContentHeight ?: 0
|
|
43
|
+
private val contentBottomInset: Int get() = delegate?.contentBottomInset ?: 0
|
|
44
|
+
private val maxContentHeight: Int? get() = delegate?.maxContentHeight
|
|
45
|
+
private val keyboardInset: Int get() = delegate?.keyboardInset ?: 0
|
|
46
|
+
private val topInset: Int get() = delegate?.topInset ?: 0
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Height for auto (-1.0) detents: content + header + footer height.
|
|
50
|
+
* An absolute (floating) header or footer overlaps the content, so it contributes no height.
|
|
51
|
+
*/
|
|
52
|
+
private val autoDetentHeight: Int
|
|
53
|
+
get() = contentHeight +
|
|
54
|
+
(if (delegate?.absoluteHeader == true) 0 else headerHeight) +
|
|
55
|
+
(if (delegate?.absoluteFooter == true) 0 else footerHeight)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Bottom inset for auto detents. A relative footer owns the sheet's bottom
|
|
59
|
+
* edge, so it absorbs the inset instead of adding it to the auto height.
|
|
60
|
+
*/
|
|
61
|
+
private val autoDetentBottomInset: Int
|
|
62
|
+
get() = if (footerHeight > 0 && delegate?.absoluteFooter == false) 0 else contentBottomInset
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Bottom inset for peek detents. An absolute footer counts toward the peek
|
|
66
|
+
* height and absorbs the inset, so it isn't added again.
|
|
67
|
+
*/
|
|
68
|
+
private val peekDetentBottomInset: Int
|
|
69
|
+
get() = if (footerHeight > 0 && delegate?.absoluteFooter == true) 0 else contentBottomInset
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Height for peek (-2.0) detents: header + footer + peek content height.
|
|
73
|
+
* A relative footer is pushed off-screen at peek, so it contributes no height.
|
|
74
|
+
* Falls back to 150dp when none is present.
|
|
75
|
+
*/
|
|
76
|
+
private val peekDetentHeight: Int
|
|
77
|
+
get() {
|
|
78
|
+
val height = headerHeight +
|
|
79
|
+
(if (delegate?.absoluteFooter == true) footerHeight else 0) +
|
|
80
|
+
peekContentHeight
|
|
81
|
+
return if (height > 0) height else DEFAULT_PEEK_HEIGHT.dpToPx().toInt()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Calculate the height in pixels for a given detent value.
|
|
86
|
+
* @param detent The detent value: -1.0 for content-fit, -2.0 for peek, or 0.0-1.0 for screen fraction
|
|
87
|
+
*/
|
|
88
|
+
fun getDetentHeight(detent: Double, includeKeyboard: Boolean = true): Int {
|
|
89
|
+
val baseHeight = if (detent == -1.0) {
|
|
90
|
+
autoDetentHeight + autoDetentBottomInset
|
|
91
|
+
} else if (detent == -2.0) {
|
|
92
|
+
peekDetentHeight + peekDetentBottomInset
|
|
93
|
+
} else {
|
|
94
|
+
if (detent <= 0.0 || detent > 1.0) {
|
|
95
|
+
throw IllegalArgumentException("TrueSheet: detent fraction ($detent) must be between 0 and 1")
|
|
96
|
+
}
|
|
97
|
+
(detent * screenHeight).toInt() + contentBottomInset
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
val height = if (includeKeyboard) baseHeight + keyboardInset else baseHeight
|
|
101
|
+
val maxAllowedHeight = screenHeight + contentBottomInset
|
|
102
|
+
return maxContentHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the expected sheet top position for a detent index.
|
|
107
|
+
*/
|
|
108
|
+
fun getSheetTopForDetentIndex(index: Int): Int {
|
|
109
|
+
if (index < 0 || index >= detents.size) {
|
|
110
|
+
RNLog.w(reactContext, "TrueSheet: Detent index ($index) is out of bounds (0..${detents.size - 1})")
|
|
111
|
+
return realScreenHeight
|
|
112
|
+
}
|
|
113
|
+
// Clamp to the space the sheet can actually occupy — matching
|
|
114
|
+
// setupSheetDetents. A keyboard-inflated detent height can exceed it,
|
|
115
|
+
// placing the expected top above the screen while the real sheet stops
|
|
116
|
+
// at the top inset.
|
|
117
|
+
val maxAvailableHeight = realScreenHeight - topInset
|
|
118
|
+
return realScreenHeight - minOf(getDetentHeight(detents[index]), maxAvailableHeight)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Calculate visible sheet height from sheet top position.
|
|
123
|
+
*/
|
|
124
|
+
fun getVisibleSheetHeight(sheetTop: Int): Int = realScreenHeight - sheetTop
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Convert visible sheet height to position in dp.
|
|
128
|
+
*/
|
|
129
|
+
fun getPositionDp(visibleSheetHeight: Int): Float = (screenHeight - visibleSheetHeight).pxToDp()
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Returns the raw screen fraction for a detent index (without bottomInset).
|
|
133
|
+
*/
|
|
134
|
+
fun getDetentValueForIndex(index: Int): Float {
|
|
135
|
+
if (index < 0 || index >= detents.size) return 0f
|
|
136
|
+
val value = detents[index]
|
|
137
|
+
return if (value == -1.0) {
|
|
138
|
+
autoDetentHeight.toFloat() / screenHeight.toFloat()
|
|
139
|
+
} else if (value == -2.0) {
|
|
140
|
+
peekDetentHeight.toFloat() / screenHeight.toFloat()
|
|
141
|
+
} else {
|
|
142
|
+
value.toFloat()
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ====================================================================
|
|
147
|
+
// MARK: - State Mapping
|
|
148
|
+
// ====================================================================
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Maps detent index to BottomSheetBehavior state based on detent count.
|
|
152
|
+
*/
|
|
153
|
+
fun getStateForDetentIndex(index: Int): Int {
|
|
154
|
+
val stateMap = getDetentStateMap() ?: return BottomSheetBehavior.STATE_HIDDEN
|
|
155
|
+
return stateMap.entries.find { it.value == index }?.key ?: BottomSheetBehavior.STATE_HIDDEN
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Maps BottomSheetBehavior state to detent index.
|
|
160
|
+
* @return The detent index, or null if state is not mapped
|
|
161
|
+
*/
|
|
162
|
+
fun getDetentIndexForState(state: Int): Int? {
|
|
163
|
+
val stateMap = getDetentStateMap() ?: return null
|
|
164
|
+
return stateMap[state]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Returns state-to-index mapping based on detent count.
|
|
169
|
+
*/
|
|
170
|
+
private fun getDetentStateMap(): Map<Int, Int>? =
|
|
171
|
+
when (detents.size) {
|
|
172
|
+
1 -> mapOf(
|
|
173
|
+
BottomSheetBehavior.STATE_COLLAPSED to 0,
|
|
174
|
+
BottomSheetBehavior.STATE_EXPANDED to 0
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
2 -> mapOf(
|
|
178
|
+
BottomSheetBehavior.STATE_COLLAPSED to 0,
|
|
179
|
+
BottomSheetBehavior.STATE_HALF_EXPANDED to 1,
|
|
180
|
+
BottomSheetBehavior.STATE_EXPANDED to 1
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
3 -> mapOf(
|
|
184
|
+
BottomSheetBehavior.STATE_COLLAPSED to 0,
|
|
185
|
+
BottomSheetBehavior.STATE_HALF_EXPANDED to 1,
|
|
186
|
+
BottomSheetBehavior.STATE_EXPANDED to 2
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
else -> null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ====================================================================
|
|
193
|
+
// MARK: - Interpolation
|
|
194
|
+
// ====================================================================
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Find which segment the position falls into for interpolation.
|
|
198
|
+
* @return Triple(fromIndex, toIndex, progress) where progress is 0-1, or null if no detents
|
|
199
|
+
*/
|
|
200
|
+
fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
201
|
+
val count = detents.size
|
|
202
|
+
if (count == 0) return null
|
|
203
|
+
|
|
204
|
+
val firstPos = getSheetTopForDetentIndex(0)
|
|
205
|
+
|
|
206
|
+
// Position is below first detent (sheet is being dragged down to dismiss)
|
|
207
|
+
if (positionPx > firstPos) {
|
|
208
|
+
val range = realScreenHeight - firstPos
|
|
209
|
+
val progress = if (range > 0) (positionPx - firstPos).toFloat() / range else 0f
|
|
210
|
+
return Triple(-1, 0, progress)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (count == 1) return Triple(0, 0, 0f)
|
|
214
|
+
|
|
215
|
+
val lastPos = getSheetTopForDetentIndex(count - 1)
|
|
216
|
+
// Position is above last detent
|
|
217
|
+
if (positionPx < lastPos) {
|
|
218
|
+
return Triple(count - 1, count - 1, 0f)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Find the segment containing this position
|
|
222
|
+
for (i in 0 until count - 1) {
|
|
223
|
+
val pos = getSheetTopForDetentIndex(i)
|
|
224
|
+
val nextPos = getSheetTopForDetentIndex(i + 1)
|
|
225
|
+
|
|
226
|
+
// Skip degenerate segments — keyboard growth can clamp adjacent detents
|
|
227
|
+
// to the same top; falling through reports the topmost detent
|
|
228
|
+
if (pos == nextPos) continue
|
|
229
|
+
|
|
230
|
+
if (positionPx in nextPos..pos) {
|
|
231
|
+
val range = pos - nextPos
|
|
232
|
+
val progress = if (range > 0) (pos - positionPx).toFloat() / range else 0f
|
|
233
|
+
return Triple(i, i + 1, maxOf(0f, minOf(1f, progress)))
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return Triple(count - 1, count - 1, 0f)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Returns continuous index (e.g., 0.5 = halfway between detent 0 and 1).
|
|
242
|
+
*/
|
|
243
|
+
fun getInterpolatedIndexForPosition(positionPx: Int): Float {
|
|
244
|
+
val count = detents.size
|
|
245
|
+
if (count == 0) return -1f
|
|
246
|
+
|
|
247
|
+
val segment = findSegmentForPosition(positionPx) ?: return 0f
|
|
248
|
+
val (fromIndex, _, progress) = segment
|
|
249
|
+
|
|
250
|
+
if (fromIndex == -1) return -progress
|
|
251
|
+
return fromIndex + progress
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Returns interpolated screen fraction for position.
|
|
256
|
+
*/
|
|
257
|
+
fun getInterpolatedDetentForPosition(positionPx: Int): Float {
|
|
258
|
+
val count = detents.size
|
|
259
|
+
if (count == 0) return 0f
|
|
260
|
+
|
|
261
|
+
val segment = findSegmentForPosition(positionPx) ?: return getDetentValueForIndex(0)
|
|
262
|
+
val (fromIndex, toIndex, progress) = segment
|
|
263
|
+
|
|
264
|
+
if (fromIndex == -1) {
|
|
265
|
+
val firstDetent = getDetentValueForIndex(0)
|
|
266
|
+
return maxOf(0f, firstDetent * (1 - progress))
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
val fromDetent = getDetentValueForIndex(fromIndex)
|
|
270
|
+
val toDetent = getDetentValueForIndex(toIndex)
|
|
271
|
+
return fromDetent + progress * (toDetent - fromDetent)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
companion object {
|
|
275
|
+
private const val DEFAULT_PEEK_HEIGHT = 150f
|
|
276
|
+
}
|
|
277
|
+
}
|