@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,162 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.graphics.Outline
|
|
6
|
+
import android.view.MotionEvent
|
|
7
|
+
import android.view.View
|
|
8
|
+
import android.view.ViewGroup
|
|
9
|
+
import android.view.ViewOutlineProvider
|
|
10
|
+
import com.facebook.react.uimanager.PointerEvents
|
|
11
|
+
import com.facebook.react.uimanager.ReactPointerEventsView
|
|
12
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
|
+
import com.lodev09.truesheet.utils.ScreenUtils
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Delegate for handling dim view interactions.
|
|
17
|
+
*/
|
|
18
|
+
interface TrueSheetDimViewDelegate {
|
|
19
|
+
fun dimViewDidTap()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Dim view that sits behind the bottom sheet in the CoordinatorLayout.
|
|
24
|
+
*
|
|
25
|
+
* Key behaviors:
|
|
26
|
+
* - When alpha > 0 (dimmed): blocks touches and calls delegate on tap
|
|
27
|
+
* - When alpha == 0 (not dimmed): passes touches through to views below
|
|
28
|
+
*
|
|
29
|
+
* This implements the "dimmedDetentIndex" equivalent functionality:
|
|
30
|
+
* the view only becomes interactive when the sheet is at or above the dimmed detent.
|
|
31
|
+
*/
|
|
32
|
+
@SuppressLint("ViewConstructor")
|
|
33
|
+
class TrueSheetDimView(private val reactContext: ThemedReactContext) :
|
|
34
|
+
View(reactContext),
|
|
35
|
+
ReactPointerEventsView {
|
|
36
|
+
|
|
37
|
+
companion object {
|
|
38
|
+
private const val MAX_ALPHA = 0.5f
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var delegate: TrueSheetDimViewDelegate? = null
|
|
42
|
+
|
|
43
|
+
private var targetView: ViewGroup? = null
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether this view should block gestures (when dimmed).
|
|
47
|
+
*/
|
|
48
|
+
private val blockGestures: Boolean
|
|
49
|
+
get() = alpha > 0f
|
|
50
|
+
|
|
51
|
+
init {
|
|
52
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
53
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
54
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
55
|
+
)
|
|
56
|
+
setBackgroundColor(Color.BLACK)
|
|
57
|
+
alpha = 0f
|
|
58
|
+
|
|
59
|
+
// Handle taps on the dim view
|
|
60
|
+
setOnClickListener {
|
|
61
|
+
delegate?.dimViewDidTap()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// MARK: - Attachment
|
|
69
|
+
// =============================================================================
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Attaches this dim view to a target view group.
|
|
73
|
+
* For CoordinatorLayout usage, pass null to use the default (activity's decor view).
|
|
74
|
+
* For stacked sheets, pass the parent sheet's bottom sheet view with corner radius.
|
|
75
|
+
*/
|
|
76
|
+
fun attach(view: ViewGroup? = null, cornerRadius: Float = 0f) {
|
|
77
|
+
if (parent != null) return
|
|
78
|
+
targetView = view ?: reactContext.currentActivity?.window?.decorView as? ViewGroup
|
|
79
|
+
|
|
80
|
+
if (cornerRadius > 0f) {
|
|
81
|
+
outlineProvider = object : ViewOutlineProvider() {
|
|
82
|
+
override fun getOutline(v: View, outline: Outline) {
|
|
83
|
+
outline.setRoundRect(0, 0, v.width, v.height, cornerRadius)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
clipToOutline = true
|
|
87
|
+
} else {
|
|
88
|
+
outlineProvider = null
|
|
89
|
+
clipToOutline = false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
targetView?.addView(this)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Attaches this dim view to a CoordinatorLayout at index 0 (behind the sheet).
|
|
97
|
+
*/
|
|
98
|
+
fun attachToCoordinator(coordinator: TrueSheetCoordinatorLayout) {
|
|
99
|
+
if (parent != null) return
|
|
100
|
+
targetView = coordinator
|
|
101
|
+
outlineProvider = null
|
|
102
|
+
clipToOutline = false
|
|
103
|
+
coordinator.addView(this, 0)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fun detach() {
|
|
107
|
+
targetView?.removeView(this)
|
|
108
|
+
targetView = null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// MARK: - Alpha Calculation
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
fun calculateAlpha(sheetTop: Int, dimmedDetentIndex: Int, getSheetTopForDetentIndex: (Int) -> Int): Float {
|
|
116
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
117
|
+
val dimmedDetentTop = getSheetTopForDetentIndex(dimmedDetentIndex)
|
|
118
|
+
val belowDimmedTop = if (dimmedDetentIndex > 0) getSheetTopForDetentIndex(dimmedDetentIndex - 1) else realHeight
|
|
119
|
+
|
|
120
|
+
return when {
|
|
121
|
+
sheetTop <= dimmedDetentTop -> MAX_ALPHA
|
|
122
|
+
|
|
123
|
+
sheetTop >= belowDimmedTop -> 0f
|
|
124
|
+
|
|
125
|
+
else -> {
|
|
126
|
+
val progress = 1f - (sheetTop - dimmedDetentTop).toFloat() / (belowDimmedTop - dimmedDetentTop)
|
|
127
|
+
(progress * MAX_ALPHA).coerceIn(0f, MAX_ALPHA)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fun interpolateAlpha(sheetTop: Int, dimmedDetentIndex: Int, getSheetTopForDetentIndex: (Int) -> Int) {
|
|
133
|
+
alpha = calculateAlpha(sheetTop, dimmedDetentIndex, getSheetTopForDetentIndex)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// MARK: - Touch Handling
|
|
138
|
+
// =============================================================================
|
|
139
|
+
|
|
140
|
+
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
141
|
+
if (blockGestures) {
|
|
142
|
+
// When dimmed, consume touch and trigger click on ACTION_UP
|
|
143
|
+
if (event.action == MotionEvent.ACTION_UP) {
|
|
144
|
+
performClick()
|
|
145
|
+
}
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
// When not dimmed, let touches pass through
|
|
149
|
+
return false
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// =============================================================================
|
|
153
|
+
// MARK: - ReactPointerEventsView
|
|
154
|
+
// =============================================================================
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* When dimmed (alpha > 0), intercept touches (AUTO).
|
|
158
|
+
* When not dimmed (alpha == 0), pass through (NONE).
|
|
159
|
+
*/
|
|
160
|
+
override val pointerEvents: PointerEvents
|
|
161
|
+
get() = if (blockGestures) PointerEvents.AUTO else PointerEvents.NONE
|
|
162
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
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.graphics.Color
|
|
7
|
+
import android.graphics.drawable.GradientDrawable
|
|
8
|
+
import android.os.Bundle
|
|
9
|
+
import android.view.Gravity
|
|
10
|
+
import android.view.View
|
|
11
|
+
import android.view.accessibility.AccessibilityNodeInfo
|
|
12
|
+
import android.widget.FrameLayout
|
|
13
|
+
import androidx.core.graphics.ColorUtils
|
|
14
|
+
import androidx.core.view.ViewCompat
|
|
15
|
+
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Options for customizing the grabber appearance.
|
|
19
|
+
*/
|
|
20
|
+
data class GrabberOptions(
|
|
21
|
+
val width: Float? = null,
|
|
22
|
+
val height: Float? = null,
|
|
23
|
+
val topMargin: Float? = null,
|
|
24
|
+
val cornerRadius: Float? = null,
|
|
25
|
+
val color: Int? = null,
|
|
26
|
+
val adaptive: Boolean = true
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Options for customizing (e.g. localizing) accessibility strings.
|
|
31
|
+
*/
|
|
32
|
+
data class AccessibilityOptions(
|
|
33
|
+
val grabberLabel: String? = null,
|
|
34
|
+
val expandedValue: String? = null,
|
|
35
|
+
val collapsedValue: String? = null,
|
|
36
|
+
val detentValue: String? = null,
|
|
37
|
+
val expandActionLabel: String? = null,
|
|
38
|
+
val collapseActionLabel: String? = null,
|
|
39
|
+
val paneTitle: String? = null
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Native grabber (drag handle) view for the bottom sheet.
|
|
44
|
+
* Displays a small pill-shaped indicator at the top of the sheet with a tappable hitbox.
|
|
45
|
+
*/
|
|
46
|
+
@SuppressLint("ViewConstructor")
|
|
47
|
+
class TrueSheetGrabberView(
|
|
48
|
+
context: Context,
|
|
49
|
+
private val options: GrabberOptions? = null,
|
|
50
|
+
private val accessibilityOptions: AccessibilityOptions? = null
|
|
51
|
+
) : FrameLayout(context) {
|
|
52
|
+
|
|
53
|
+
companion object {
|
|
54
|
+
private const val DEFAULT_WIDTH = 32f // dp
|
|
55
|
+
private const val DEFAULT_HEIGHT = 4f // dp
|
|
56
|
+
private const val DEFAULT_TOP_MARGIN = 16f // dp
|
|
57
|
+
private const val DEFAULT_ALPHA = 0.4f
|
|
58
|
+
private const val HITBOX_PADDING_HORIZONTAL = 16f // dp
|
|
59
|
+
private const val HITBOX_PADDING_VERTICAL = 16f // dp
|
|
60
|
+
private val DEFAULT_COLOR = Color.argb((DEFAULT_ALPHA * 255).toInt(), 73, 69, 79) // #49454F @ 40%
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private val grabberWidth: Float
|
|
64
|
+
get() = options?.width ?: DEFAULT_WIDTH
|
|
65
|
+
|
|
66
|
+
private val grabberHeight: Float
|
|
67
|
+
get() = options?.height ?: DEFAULT_HEIGHT
|
|
68
|
+
|
|
69
|
+
private val grabberTopMargin: Float
|
|
70
|
+
get() = options?.topMargin ?: DEFAULT_TOP_MARGIN
|
|
71
|
+
|
|
72
|
+
private val grabberCornerRadius: Float
|
|
73
|
+
get() = options?.cornerRadius ?: (grabberHeight / 2)
|
|
74
|
+
|
|
75
|
+
private val isAdaptive: Boolean
|
|
76
|
+
get() = options?.adaptive ?: true
|
|
77
|
+
|
|
78
|
+
private val grabberColor: Int
|
|
79
|
+
get() = if (isAdaptive) getAdaptiveColor(options?.color) else options?.color ?: DEFAULT_COLOR
|
|
80
|
+
|
|
81
|
+
var onAccessibilityIncrement: (() -> Unit)? = null
|
|
82
|
+
var onAccessibilityDecrement: (() -> Unit)? = null
|
|
83
|
+
|
|
84
|
+
init {
|
|
85
|
+
val hitboxWidth = grabberWidth + (HITBOX_PADDING_HORIZONTAL * 2)
|
|
86
|
+
val hitboxHeight = grabberHeight + (HITBOX_PADDING_VERTICAL * 2)
|
|
87
|
+
|
|
88
|
+
layoutParams = LayoutParams(
|
|
89
|
+
hitboxWidth.dpToPx().toInt(),
|
|
90
|
+
hitboxHeight.dpToPx().toInt()
|
|
91
|
+
).apply {
|
|
92
|
+
gravity = Gravity.CENTER_HORIZONTAL or Gravity.TOP
|
|
93
|
+
topMargin = (grabberTopMargin - HITBOX_PADDING_VERTICAL).dpToPx().toInt()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The visible pill centered inside the hitbox
|
|
97
|
+
val pillView = View(context).apply {
|
|
98
|
+
layoutParams = LayoutParams(
|
|
99
|
+
grabberWidth.dpToPx().toInt(),
|
|
100
|
+
grabberHeight.dpToPx().toInt()
|
|
101
|
+
).apply {
|
|
102
|
+
gravity = Gravity.CENTER
|
|
103
|
+
}
|
|
104
|
+
background = GradientDrawable().apply {
|
|
105
|
+
shape = GradientDrawable.RECTANGLE
|
|
106
|
+
cornerRadius = grabberCornerRadius.dpToPx()
|
|
107
|
+
setColor(grabberColor)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
addView(pillView)
|
|
112
|
+
|
|
113
|
+
isFocusable = true
|
|
114
|
+
contentDescription = accessibilityOptions?.grabberLabel ?: "Drag handle"
|
|
115
|
+
|
|
116
|
+
accessibilityDelegate = object : View.AccessibilityDelegate() {
|
|
117
|
+
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
|
|
118
|
+
super.onInitializeAccessibilityNodeInfo(host, info)
|
|
119
|
+
info.addAction(
|
|
120
|
+
AccessibilityNodeInfo.AccessibilityAction(
|
|
121
|
+
AccessibilityNodeInfo.ACTION_SCROLL_FORWARD,
|
|
122
|
+
accessibilityOptions?.expandActionLabel ?: "Expand"
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
info.addAction(
|
|
126
|
+
AccessibilityNodeInfo.AccessibilityAction(
|
|
127
|
+
AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD,
|
|
128
|
+
accessibilityOptions?.collapseActionLabel ?: "Collapse"
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
info.className = "android.widget.SeekBar"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean =
|
|
135
|
+
when (action) {
|
|
136
|
+
AccessibilityNodeInfo.ACTION_SCROLL_FORWARD -> {
|
|
137
|
+
onAccessibilityIncrement?.invoke()
|
|
138
|
+
true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD -> {
|
|
142
|
+
onAccessibilityDecrement?.invoke()
|
|
143
|
+
true
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
else -> super.performAccessibilityAction(host, action, args)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fun updateAccessibilityValue(index: Int, detentCount: Int) {
|
|
152
|
+
val description: CharSequence? = when {
|
|
153
|
+
index < 0 || detentCount <= 0 -> null
|
|
154
|
+
|
|
155
|
+
index >= detentCount - 1 -> accessibilityOptions?.expandedValue ?: "Expanded"
|
|
156
|
+
|
|
157
|
+
index == 0 -> accessibilityOptions?.collapsedValue ?: "Collapsed"
|
|
158
|
+
|
|
159
|
+
else ->
|
|
160
|
+
(accessibilityOptions?.detentValue ?: "Detent {index} of {count}")
|
|
161
|
+
.replace("{index}", "${index + 1}")
|
|
162
|
+
.replace("{count}", "$detentCount")
|
|
163
|
+
}
|
|
164
|
+
ViewCompat.setStateDescription(this, description)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private fun getAdaptiveColor(baseColor: Int? = null): Int {
|
|
168
|
+
val nightMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
|
169
|
+
val isDarkMode = nightMode == Configuration.UI_MODE_NIGHT_YES
|
|
170
|
+
val modeColor = if (isDarkMode) Color.WHITE else Color.BLACK
|
|
171
|
+
|
|
172
|
+
return if (baseColor != null) {
|
|
173
|
+
// Blend user color with mode color for adaptive effect
|
|
174
|
+
val blendedColor = ColorUtils.blendARGB(baseColor, modeColor, 0.3f)
|
|
175
|
+
ColorUtils.setAlphaComponent(blendedColor, (DEFAULT_ALPHA * 255).toInt())
|
|
176
|
+
} else {
|
|
177
|
+
ColorUtils.setAlphaComponent(modeColor, (DEFAULT_ALPHA * 255).toInt())
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import android.graphics.Rect
|
|
4
|
+
import android.os.Build
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.view.ViewTreeObserver
|
|
7
|
+
import androidx.core.view.ViewCompat
|
|
8
|
+
import androidx.core.view.WindowInsetsAnimationCompat
|
|
9
|
+
import androidx.core.view.WindowInsetsCompat
|
|
10
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
|
+
import com.lodev09.truesheet.utils.KeyboardUtils
|
|
12
|
+
import com.lodev09.truesheet.utils.isDescendantOf
|
|
13
|
+
|
|
14
|
+
interface TrueSheetKeyboardObserverDelegate {
|
|
15
|
+
fun keyboardWillShow(height: Int) {}
|
|
16
|
+
fun keyboardDidShow(height: Int) {}
|
|
17
|
+
fun keyboardWillHide() {}
|
|
18
|
+
fun keyboardDidHide() {}
|
|
19
|
+
fun keyboardDidChangeHeight(height: Int) {}
|
|
20
|
+
fun focusDidChange(newFocus: View) {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Tracks keyboard height and notifies delegate on changes.
|
|
25
|
+
* Uses WindowInsetsAnimationCompat on API 30+, ViewTreeObserver fallback on older versions.
|
|
26
|
+
*/
|
|
27
|
+
class TrueSheetKeyboardObserver(private val targetView: View, private val reactContext: ThemedReactContext) {
|
|
28
|
+
|
|
29
|
+
var delegate: TrueSheetKeyboardObserverDelegate? = null
|
|
30
|
+
|
|
31
|
+
var currentHeight: Int = 0
|
|
32
|
+
private set
|
|
33
|
+
|
|
34
|
+
var targetHeight: Int = 0
|
|
35
|
+
private set
|
|
36
|
+
|
|
37
|
+
var isTransitioning: Boolean = false
|
|
38
|
+
private set
|
|
39
|
+
|
|
40
|
+
fun isFocusedViewWithinSheet(sheetView: View): Boolean {
|
|
41
|
+
val focusedView = targetView.rootView?.findFocus() ?: return false
|
|
42
|
+
var current: View? = focusedView
|
|
43
|
+
while (current != null && current !== targetView) {
|
|
44
|
+
if (current === sheetView) return true
|
|
45
|
+
val parent = current.parent
|
|
46
|
+
current = if (parent is View) parent else null
|
|
47
|
+
}
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var isHiding: Boolean = false
|
|
52
|
+
private set
|
|
53
|
+
private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
|
54
|
+
private var focusChangeListener: ViewTreeObserver.OnGlobalFocusChangeListener? = null
|
|
55
|
+
private var activityRootView: View? = null
|
|
56
|
+
|
|
57
|
+
fun start() {
|
|
58
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
59
|
+
setupAnimationCallback()
|
|
60
|
+
} else {
|
|
61
|
+
setupLegacyListener()
|
|
62
|
+
}
|
|
63
|
+
setupFocusChangeListener()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fun stop() {
|
|
67
|
+
try {
|
|
68
|
+
globalLayoutListener?.let { listener ->
|
|
69
|
+
activityRootView?.viewTreeObserver?.let { observer ->
|
|
70
|
+
if (observer.isAlive) observer.removeOnGlobalLayoutListener(listener)
|
|
71
|
+
}
|
|
72
|
+
globalLayoutListener = null
|
|
73
|
+
}
|
|
74
|
+
focusChangeListener?.let { listener ->
|
|
75
|
+
activityRootView?.viewTreeObserver?.let { observer ->
|
|
76
|
+
if (observer.isAlive) observer.removeOnGlobalFocusChangeListener(listener)
|
|
77
|
+
}
|
|
78
|
+
focusChangeListener = null
|
|
79
|
+
}
|
|
80
|
+
} finally {
|
|
81
|
+
activityRootView = null
|
|
82
|
+
ViewCompat.setWindowInsetsAnimationCallback(targetView, null)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private fun updateHeight(from: Int, to: Int, fraction: Float) {
|
|
87
|
+
val newHeight = (from + (to - from) * fraction).toInt()
|
|
88
|
+
if (currentHeight != newHeight) {
|
|
89
|
+
currentHeight = newHeight
|
|
90
|
+
delegate?.keyboardDidChangeHeight(newHeight)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private fun getKeyboardHeight(): Int = KeyboardUtils.getKeyboardHeight(targetView)
|
|
95
|
+
|
|
96
|
+
private fun setupAnimationCallback() {
|
|
97
|
+
ViewCompat.setWindowInsetsAnimationCallback(
|
|
98
|
+
targetView,
|
|
99
|
+
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
|
|
100
|
+
private var startHeight = 0
|
|
101
|
+
private var endHeight = 0
|
|
102
|
+
|
|
103
|
+
override fun onPrepare(animation: WindowInsetsAnimationCompat) {
|
|
104
|
+
startHeight = getKeyboardHeight()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
override fun onStart(
|
|
108
|
+
animation: WindowInsetsAnimationCompat,
|
|
109
|
+
bounds: WindowInsetsAnimationCompat.BoundsCompat
|
|
110
|
+
): WindowInsetsAnimationCompat.BoundsCompat {
|
|
111
|
+
endHeight = getKeyboardHeight()
|
|
112
|
+
targetHeight = endHeight
|
|
113
|
+
isHiding = endHeight < startHeight
|
|
114
|
+
isTransitioning = true
|
|
115
|
+
if (endHeight > startHeight) {
|
|
116
|
+
delegate?.keyboardWillShow(endHeight)
|
|
117
|
+
} else if (isHiding) {
|
|
118
|
+
delegate?.keyboardWillHide()
|
|
119
|
+
}
|
|
120
|
+
return bounds
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override fun onProgress(insets: WindowInsetsCompat, runningAnimations: List<WindowInsetsAnimationCompat>): WindowInsetsCompat {
|
|
124
|
+
val imeAnimation = runningAnimations.find {
|
|
125
|
+
it.typeMask and WindowInsetsCompat.Type.ime() != 0
|
|
126
|
+
} ?: return insets
|
|
127
|
+
|
|
128
|
+
val fraction = imeAnimation.interpolatedFraction
|
|
129
|
+
updateHeight(startHeight, endHeight, fraction)
|
|
130
|
+
|
|
131
|
+
return insets
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override fun onEnd(animation: WindowInsetsAnimationCompat) {
|
|
135
|
+
val finalHeight = getKeyboardHeight()
|
|
136
|
+
updateHeight(startHeight, finalHeight, 1f)
|
|
137
|
+
isTransitioning = false
|
|
138
|
+
if (isHiding) {
|
|
139
|
+
delegate?.keyboardDidHide()
|
|
140
|
+
isHiding = false
|
|
141
|
+
} else if (finalHeight > 0) {
|
|
142
|
+
delegate?.keyboardDidShow(finalHeight)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private fun setupLegacyListener() {
|
|
150
|
+
// Ensure we don't add duplicate listeners
|
|
151
|
+
if (globalLayoutListener != null) return
|
|
152
|
+
|
|
153
|
+
val rootView = targetView.rootView ?: return
|
|
154
|
+
activityRootView = rootView
|
|
155
|
+
|
|
156
|
+
globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
|
|
157
|
+
val rect = Rect()
|
|
158
|
+
rootView.getWindowVisibleDisplayFrame(rect)
|
|
159
|
+
|
|
160
|
+
val screenHeight = rootView.height
|
|
161
|
+
val keyboardHeight = screenHeight - rect.bottom
|
|
162
|
+
|
|
163
|
+
val newHeight = if (keyboardHeight > screenHeight * 0.15) keyboardHeight else 0
|
|
164
|
+
|
|
165
|
+
// Skip if already at this height
|
|
166
|
+
if (targetHeight == newHeight) return@OnGlobalLayoutListener
|
|
167
|
+
|
|
168
|
+
val previousHeight = currentHeight
|
|
169
|
+
targetHeight = newHeight
|
|
170
|
+
isHiding = newHeight < previousHeight
|
|
171
|
+
|
|
172
|
+
isTransitioning = true
|
|
173
|
+
if (newHeight > previousHeight) {
|
|
174
|
+
delegate?.keyboardWillShow(newHeight)
|
|
175
|
+
} else if (isHiding) {
|
|
176
|
+
delegate?.keyboardWillHide()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// On legacy API, keyboard has already animated - just update immediately
|
|
180
|
+
updateHeight(previousHeight, newHeight, 1f)
|
|
181
|
+
isTransitioning = false
|
|
182
|
+
|
|
183
|
+
if (isHiding && newHeight == 0) {
|
|
184
|
+
delegate?.keyboardDidHide()
|
|
185
|
+
isHiding = false
|
|
186
|
+
} else if (newHeight > 0) {
|
|
187
|
+
delegate?.keyboardDidShow(newHeight)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
rootView.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private fun setupFocusChangeListener() {
|
|
195
|
+
if (focusChangeListener != null) return
|
|
196
|
+
|
|
197
|
+
val rootView = targetView.rootView ?: return
|
|
198
|
+
activityRootView = rootView
|
|
199
|
+
|
|
200
|
+
focusChangeListener = ViewTreeObserver.OnGlobalFocusChangeListener { _, newFocus ->
|
|
201
|
+
if (currentHeight > 0 && newFocus != null && newFocus.isDescendantOf(targetView)) {
|
|
202
|
+
delegate?.focusDidChange(newFocus)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
rootView.viewTreeObserver.addOnGlobalFocusChangeListener(focusChangeListener)
|
|
206
|
+
}
|
|
207
|
+
}
|