@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.11
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/ApexNavigation.podspec +2 -1
- package/CHANGELOG.md +132 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +78 -25
- package/android/CORE.md +49 -25
- package/android/RECIPES.md +13 -6
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +85 -10
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/options/SideMenuOptions.kt +6 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +35 -23
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +12 -7
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +42 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +54 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -50
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +50 -81
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +135 -94
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +39 -19
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +16 -4
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementDefinition.kt +9 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementPlanner.kt +20 -15
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +108 -34
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +109 -21
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +476 -73
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +57 -25
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +83 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +49 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +71 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +37 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +42 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +19 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +43 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +77 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +315 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +30 -20
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +216 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +149 -41
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +115 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +222 -48
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +30 -34
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +219 -54
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +9 -7
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +143 -135
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +112 -4
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +56 -41
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
- package/android/src/main/java/dev/apexnative/navigation/views/sidemenu/SideMenuView.kt +12 -3
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +45 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +140 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -17
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +73 -34
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +125 -178
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
- package/android/src/main/jni/ANNContentHeights.cpp +82 -0
- package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
- package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
- package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
- package/android/src/main/res/values/styles.xml +1 -1
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/cli/app.js +1 -1
- package/cli/doctor/checks/app.js +77 -14
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/project.js +10 -2
- package/cli/setup/index.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/setup/steps/metro.js +46 -0
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/native-screens.md +4 -0
- package/docs-md/architecture/theme-and-context.md +131 -12
- package/docs-md/architecture/transitions.md +58 -12
- package/docs-md/containers/arrangement.md +17 -1
- package/docs-md/containers/modals.md +8 -4
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +19 -2
- package/docs-md/containers/tabs.md +5 -1
- package/docs-md/flows/bottom-sheets.md +4 -2
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/fundamentals/screens-and-props.md +9 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +4 -3
- package/docs-md/guides/deep-links.md +15 -33
- package/docs-md/help/glossary.md +4 -4
- package/docs-md/help/troubleshooting.md +44 -6
- package/docs-md/index.json +61 -1
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +9 -3
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +46 -2
- package/docs-md/screen-ui/bar-buttons.md +5 -5
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +17 -12
- package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +21 -3
- package/ios/Animations/ANNFrameDriver.h +55 -15
- package/ios/Animations/ANNFrameDriver.mm +216 -60
- package/ios/Animations/ANNTransitionAnimator.h +26 -5
- package/ios/Animations/ANNTransitionAnimator.mm +61 -13
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- package/ios/Animations/ANNTransitionWait.mm +6 -7
- package/ios/Animations/Elements/ANNElementFinder.h +57 -0
- package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
- package/ios/Animations/Elements/ANNElementTransition.h +10 -8
- package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
- package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
- package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
- package/ios/CLAUDE.md +60 -24
- package/ios/CORE.md +44 -34
- package/ios/Contract/ANNCommandAnswer.h +13 -5
- package/ios/Contract/ANNCommandAnswer.mm +40 -32
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Contract/ANNCommandRunner.h +10 -9
- package/ios/Contract/ANNCommandRunner.mm +22 -28
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNDeepLinkQueue.h +22 -2
- package/ios/Core/ANNDeepLinkQueue.mm +121 -14
- package/ios/Core/ANNErrorHandler.h +9 -4
- package/ios/Core/ANNErrorHandler.mm +20 -2
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +6 -2
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.h +6 -0
- package/ios/Core/ANNHinge.mm +15 -4
- package/ios/Core/ANNJavaScriptCalls.h +33 -0
- package/ios/Core/ANNJavaScriptCalls.mm +52 -0
- package/ios/Core/ANNLayoutProtocol.h +5 -5
- package/ios/Core/ANNNavigationState.h +7 -5
- package/ios/Core/ANNNavigationState.mm +8 -7
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNParking.h +5 -4
- package/ios/Core/ANNReloadCoordinator.mm +5 -12
- package/ios/Core/ANNRenderWait.h +20 -6
- package/ios/Core/ANNRenderWait.mm +143 -56
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Core/ANNUtils.h +7 -0
- package/ios/Core/ANNUtils.mm +10 -0
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Core/ApexNavigation.mm +7 -6
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +195 -21
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
- package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
- package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
- package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
- package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +2 -8
- package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
- package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Screen/ANNScreenSettling.h +34 -0
- package/ios/Layouts/Screen/ANNScreenSettling.mm +171 -0
- package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
- package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +184 -16
- package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
- package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
- package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNAnimationOptions.h +2 -1
- package/ios/Options/ANNAnimationOptions.mm +1 -1
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +25 -0
- package/ios/Options/ANNOptions.mm +157 -1
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNOptionsReader.mm +108 -0
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Options/ANNSideMenuOptions.h +2 -0
- package/ios/Options/ANNSideMenuOptions.mm +2 -0
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/ColorParser.h +4 -3
- package/ios/Options/Params/ColorParser.mm +22 -4
- package/ios/Options/Params/Image.h +15 -2
- package/ios/Options/Params/Image.mm +35 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +13 -6
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
- package/ios/React/ANNSurfaceHostingView.h +14 -0
- package/ios/React/ANNSurfaceHostingView.mm +144 -1
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/React/Slots/ANNSlotHost.mm +1 -3
- package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +14 -8
- package/ios/TopBar/ANNBarButtons.mm +397 -80
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +50 -7
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +172 -21
- package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
- package/ios/TopBar/ANNTopBarPresenter.h +8 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +104 -33
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
- package/lib/module/ARCHITECTURE.md +40 -27
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/app/AppRegistry.js +14 -2
- package/lib/module/api/app/AppRegistry.js.map +1 -1
- package/lib/module/api/app/createNavigation.js +190 -66
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/events/navigationEvents.js +6 -2
- package/lib/module/api/events/navigationEvents.js.map +1 -1
- package/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/linking/dispatchedLinks.js +23 -0
- package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
- package/lib/module/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/api/screens/arrangement.js +5 -2
- package/lib/module/api/screens/arrangement.js.map +1 -1
- package/lib/module/api/screens/defineScreen.js +45 -3
- package/lib/module/api/screens/defineScreen.js.map +1 -1
- package/lib/module/api/screens/nativeScreens.js +31 -6
- package/lib/module/api/screens/nativeScreens.js.map +1 -1
- package/lib/module/api/screens/registerScreens.js +193 -35
- package/lib/module/api/screens/registerScreens.js.map +1 -1
- package/lib/module/api/theme/theme.js +5 -0
- package/lib/module/api/theme/theme.js.map +1 -1
- package/lib/module/engine/commands.js +51 -9
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +20 -11
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/nativeScreens.js +9 -0
- package/lib/module/engine/nativeScreens.js.map +1 -1
- package/lib/module/engine/options.js +49 -15
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/optionsChange.js +102 -0
- package/lib/module/engine/optionsChange.js.map +1 -0
- package/lib/module/engine/places.js +52 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +61 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +77 -16
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +8 -3
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +3 -3
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/index.d.ts +1 -1
- package/lib/typescript/api/index.d.ts.map +1 -1
- package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
- package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
- package/lib/typescript/api/linking/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/api/screens/arrangement.d.ts +12 -2
- package/lib/typescript/api/screens/arrangement.d.ts.map +1 -1
- package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
- package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
- package/lib/typescript/api/theme/theme.d.ts +2 -0
- package/lib/typescript/api/theme/theme.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts +5 -0
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/layout.d.ts.map +1 -1
- package/lib/typescript/engine/nativeScreens.d.ts +5 -0
- package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- package/lib/typescript/engine/places.d.ts +7 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/refresh.d.ts +35 -0
- package/lib/typescript/engine/refresh.d.ts.map +1 -0
- package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +27 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +28 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +80 -22
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +34 -26
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +17 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +18 -11
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/metro/acceptPlugin.js +57 -0
- package/metro/babelTransformer.js +45 -0
- package/metro/index.js +36 -0
- package/package.json +3 -1
- package/src/ARCHITECTURE.md +40 -27
- package/src/adapters/NativeANNTurboModule.ts +12 -2
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +203 -69
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +10 -4
- package/src/api/hooks/useNavigation.ts +9 -4
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/navigation.ts +4 -1
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/api/screens/arrangement.ts +17 -4
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +185 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +70 -13
- package/src/engine/layout.ts +27 -11
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +45 -19
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/places.ts +65 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +74 -1
- package/src/engine/screens.tsx +76 -21
- package/src/interfaces/ComponentEvents.ts +4 -4
- package/src/interfaces/Events.ts +29 -10
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +81 -22
- package/src/interfaces/options/bottomTabs.ts +21 -7
- package/src/interfaces/options/buttons.ts +23 -15
- package/src/interfaces/options/modals.ts +34 -26
- package/src/interfaces/options/sideMenu.ts +24 -6
- package/src/interfaces/options/stack.ts +17 -3
- package/src/interfaces/options/system.ts +18 -11
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +68 -38
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
|
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.graphics.Color
|
|
6
6
|
import android.graphics.Outline
|
|
7
|
+
import android.os.SystemClock
|
|
7
8
|
import android.view.GestureDetector
|
|
8
9
|
import android.view.Gravity
|
|
9
10
|
import android.view.MotionEvent
|
|
@@ -15,8 +16,10 @@ import android.view.ViewOutlineProvider
|
|
|
15
16
|
import android.view.ViewTreeObserver
|
|
16
17
|
import android.widget.FrameLayout
|
|
17
18
|
import android.widget.ScrollView
|
|
19
|
+
import androidx.activity.BackEventCompat
|
|
18
20
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
19
21
|
import androidx.core.graphics.Insets
|
|
22
|
+
import androidx.core.view.OneShotPreDrawListener
|
|
20
23
|
import androidx.core.view.ViewCompat
|
|
21
24
|
import androidx.core.view.WindowInsetsCompat
|
|
22
25
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
@@ -24,22 +27,34 @@ import com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
|
|
24
27
|
import dev.apexnative.navigation.options.SheetDetent
|
|
25
28
|
import dev.apexnative.navigation.options.SheetOptions
|
|
26
29
|
import dev.apexnative.navigation.utils.MaterialTheme
|
|
30
|
+
import dev.apexnative.navigation.utils.SystemAnimations
|
|
27
31
|
import dev.apexnative.navigation.utils.TestIds
|
|
28
32
|
import dev.apexnative.navigation.utils.dp
|
|
33
|
+
import dev.apexnative.navigation.viewcontrollers.viewcontroller.IReactView
|
|
29
34
|
import kotlin.math.roundToInt
|
|
30
35
|
|
|
31
36
|
/**
|
|
32
|
-
* A modal shown as a Material bottom sheet (`modal.sheet`
|
|
33
|
-
* [BottomSheetBehavior], in the modals' CoordinatorLayout, over a scrim. It stops at
|
|
34
|
-
* `medium` at half the height, `large` at the full height under the status bar, a number
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
+
* A modal shown as a Material bottom sheet (`pageSheet`, `formSheet`, `popover`, or `modal.sheet` without a style): the
|
|
38
|
+
* modal's view in a container with a [BottomSheetBehavior], in the modals' CoordinatorLayout, over a scrim. It stops at
|
|
39
|
+
* the detents of its options — `medium` at half the height, `large` at the full height under the status bar, a number
|
|
40
|
+
* at that height in dp, `content` at the height of the shown screen's content — and the user drags it between them; at
|
|
41
|
+
* three of them at most, the Material behavior's resting states ([SheetStops]). The content is measured without a
|
|
42
|
+
* height limit, as on iOS ([IReactView.contentSize]), again after each of its mounts, and the stops follow it. In a
|
|
43
|
+
* window wider than [MAX_WIDTH_DP] it stands in the middle at that width. While the keyboard shows, the sheet stands
|
|
44
|
+
* on it at every stop, as on iOS, unless its screen keeps its size for it. A drag down past the smallest one closes
|
|
45
|
+
* it, unless `swipeToDismiss` is false: then the sheet stays, and the try is told. The screen under it stays in place.
|
|
46
|
+
* On Android 14+ the back gesture previews its hide ([SheetBack]): the Material behavior shrinks it with the progress
|
|
47
|
+
* and slides it down at the release.
|
|
37
48
|
*/
|
|
38
49
|
internal class ModalSheet(
|
|
39
50
|
private val layout: CoordinatorLayout,
|
|
40
51
|
private val content: View,
|
|
41
52
|
private val events: Events,
|
|
42
|
-
|
|
53
|
+
/** The content of the screen the modal shows now, which the `content` detent measures; none without one. */
|
|
54
|
+
private val shownContent: () -> IReactView?,
|
|
55
|
+
/** The screen the modal shows now moves its content out of the keyboard's way (`layout.adjustResize`). */
|
|
56
|
+
private val adjustsForKeyboard: () -> Boolean,
|
|
57
|
+
) : SheetBack {
|
|
43
58
|
interface Events {
|
|
44
59
|
/** The user dragged the sheet to another detent. */
|
|
45
60
|
fun onDetentChanged(detent: SheetDetent)
|
|
@@ -60,16 +75,75 @@ internal class ModalSheet(
|
|
|
60
75
|
private var options = SheetOptions()
|
|
61
76
|
private var swipeToDismiss = true
|
|
62
77
|
private var detents: List<SheetDetent> = listOf(SheetDetent.Large)
|
|
78
|
+
// The detents the sheet rests at, from the lowest (`SheetStops.stops`): the behavior's states.
|
|
79
|
+
private var stops: List<SheetDetent> = listOf(SheetDetent.Large)
|
|
80
|
+
// The heights the behavior has for the stops, in pixels: lifted by the keyboard (`SheetStops.lifted`).
|
|
81
|
+
private var stopHeights: List<Int> = emptyList()
|
|
82
|
+
// The part of the keyboard over the navigation bar, in pixels; 0 without the keyboard.
|
|
83
|
+
private var keyboard = 0
|
|
84
|
+
// The stops' heights changed while the sheet moved (a drag, a slide): it takes them once it rests, and goes to the
|
|
85
|
+
// `selectedDetent` a merge named meanwhile ([pendingArrived]).
|
|
86
|
+
private var restopPending = false
|
|
87
|
+
// The sheet showed while its `content` detent had no height yet: once it has one, the sheet goes where the show
|
|
88
|
+
// would have taken it ([showTarget]), unless the user moved it first.
|
|
89
|
+
private var awaiting = false
|
|
90
|
+
private var pendingArrived: SheetDetent? = null
|
|
63
91
|
private var selected: SheetDetent? = null
|
|
64
92
|
private var topGap = 0
|
|
65
93
|
private var dragged = false
|
|
66
94
|
private var shown: (() -> Unit)? = null
|
|
67
95
|
private var closing: (() -> Unit)? = null
|
|
96
|
+
// The back gesture whose preview runs now; none.
|
|
97
|
+
private var backPreview: BackPreview? = null
|
|
98
|
+
// Released: the behavior slides the sheet down from where the preview left it, and hides it at the end.
|
|
99
|
+
private var backFinishing = false
|
|
100
|
+
// The window's observer the scrim follows the preview's frames through, from its start until the sheet is back
|
|
101
|
+
// in place or gone; none otherwise.
|
|
102
|
+
private var backFramesObserver: ViewTreeObserver? = null
|
|
103
|
+
|
|
104
|
+
// The behavior tells no slide while back's preview scales the sheet and its release slides it down (they move it by
|
|
105
|
+
// its transform, not its offset): the scrim follows what is drawn, frame by frame, until the sheet stands again.
|
|
106
|
+
private val backFrames = ViewTreeObserver.OnPreDrawListener {
|
|
107
|
+
updateScrim()
|
|
108
|
+
if (backPreview == null && !backFinishing && container.scaleY == 1f && container.translationY == 0f) {
|
|
109
|
+
stopBackFrames()
|
|
110
|
+
}
|
|
111
|
+
true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private val sheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
|
|
115
|
+
override fun onStateChanged(sheet: View, newState: Int) = onState(newState)
|
|
116
|
+
override fun onSlide(sheet: View, slideOffset: Float) = updateScrim()
|
|
117
|
+
}
|
|
68
118
|
|
|
69
119
|
// A list inside the sheet scrolls with it, as the Material behavior expects: up at half height the sheet goes up
|
|
70
120
|
// first, down at the top of the list the sheet comes down. The behavior follows views that scroll nested.
|
|
71
121
|
private val scrollViews = ViewTreeObserver.OnGlobalLayoutListener {
|
|
72
|
-
|
|
122
|
+
turnScrollViews()
|
|
123
|
+
// The bars above the content may have taken their height.
|
|
124
|
+
if (followed != null) contentChanged()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The shown screen's content that the `content` detent follows, its height without a height limit (pixels, none
|
|
128
|
+
// before it is measured), and the height of the detent the behavior has now.
|
|
129
|
+
private var followed: IReactView? = null
|
|
130
|
+
// The shown screen's content whose mounts turn the lists in it to scroll nested ([hearContent]).
|
|
131
|
+
private var heard: IReactView? = null
|
|
132
|
+
private var contentBody: Int? = null
|
|
133
|
+
private var appliedContentHeight: Int? = null
|
|
134
|
+
// When the content was measured last after a mount, and whether a measure waits ([contentMounted]).
|
|
135
|
+
private var lastMeasure = 0L
|
|
136
|
+
private var measureWaits = false
|
|
137
|
+
private val measureLater = Runnable {
|
|
138
|
+
measureWaits = false
|
|
139
|
+
lastMeasure = SystemClock.uptimeMillis()
|
|
140
|
+
measureContent()
|
|
141
|
+
contentChanged()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The stops stand in pixels: they follow the layout's height (a rotation, a resize).
|
|
145
|
+
private val layoutHeight = View.OnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
|
146
|
+
if (bottom - top != oldBottom - oldTop) container.post { restop() }
|
|
73
147
|
}
|
|
74
148
|
|
|
75
149
|
/** The sheet is down, out of sight: the modal can go at once. */
|
|
@@ -88,6 +162,26 @@ internal class ModalSheet(
|
|
|
88
162
|
}
|
|
89
163
|
})
|
|
90
164
|
scrim.setOnTouchListener { _, event -> taps.onTouchEvent(event) }
|
|
165
|
+
// The keyboard covers the bottom of the window: the sheet stands on it, by the keyboard's part over the
|
|
166
|
+
// navigation bar (the detents count that bar already), unless the screen in it keeps its size for the keyboard
|
|
167
|
+
// (`layout.adjustResize: false`). The scrim, under the sheet, hears the window's insets.
|
|
168
|
+
ViewCompat.setOnApplyWindowInsetsListener(scrim) { _, insets ->
|
|
169
|
+
val ime = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
|
170
|
+
val bar = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
|
|
171
|
+
val lift = (ime - bar).coerceAtLeast(0)
|
|
172
|
+
if (lift != keyboard) {
|
|
173
|
+
keyboard = lift
|
|
174
|
+
restop()
|
|
175
|
+
}
|
|
176
|
+
insets
|
|
177
|
+
}
|
|
178
|
+
// The sheet keeps a touch that lands on it and no view in it takes (its bar, an empty part), as Material's
|
|
179
|
+
// BottomSheetDialog does: else the touch falls to the scrim under it, and a tap there closes a dimmed sheet;
|
|
180
|
+
// past an undimmed scrim it reaches the behavior, which takes the sheet into its drag state at the touch's
|
|
181
|
+
// down, before any move. The back gesture starts with such a touch, and a sheet in the drag state has no
|
|
182
|
+
// preview. A drag that starts there still moves the sheet: the behavior takes it past the touch slop, as from
|
|
183
|
+
// the views in it.
|
|
184
|
+
container.setOnTouchListener { _, _ -> true }
|
|
91
185
|
container.clipToOutline = true
|
|
92
186
|
container.outlineProvider = object : ViewOutlineProvider() {
|
|
93
187
|
// The bottom corners are under the screen's bottom: only the top ones round.
|
|
@@ -96,74 +190,109 @@ internal class ModalSheet(
|
|
|
96
190
|
outline.setRoundRect(0, 0, view.width, view.height + radius.roundToInt(), radius)
|
|
97
191
|
}
|
|
98
192
|
}
|
|
99
|
-
// The sheet is under the status bar: its screens leave no room for it at their top.
|
|
193
|
+
// The sheet is under the status bar: its screens leave no room for it at their top. In the middle of a wide
|
|
194
|
+
// window it reaches no side, and keeps room only for the part of a side bar that is over it.
|
|
100
195
|
ViewCompat.setOnApplyWindowInsetsListener(container) { _, insets ->
|
|
101
196
|
val types = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
|
|
102
197
|
val bars = insets.getInsets(types)
|
|
103
|
-
|
|
198
|
+
val laidOut = container.width > 0
|
|
199
|
+
val left = if (laidOut) (bars.left - container.left).coerceAtLeast(0) else bars.left
|
|
200
|
+
val right = if (laidOut) (bars.right - (layout.width - container.right)).coerceAtLeast(0) else bars.right
|
|
201
|
+
WindowInsetsCompat.Builder(insets).setInsets(types, Insets.of(left, 0, right, bars.bottom)).build()
|
|
202
|
+
}
|
|
203
|
+
// Its sides move with the window's width (a rotation, a resize): the room for the side bars follows.
|
|
204
|
+
container.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
|
|
205
|
+
if (left != oldLeft || right != oldRight) container.post { ViewCompat.requestApplyInsets(container) }
|
|
104
206
|
}
|
|
105
207
|
container.addView(content, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
|
|
106
208
|
container.addView(grabber, FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.TOP or Gravity.CENTER_HORIZONTAL))
|
|
107
209
|
behavior.isHideable = true
|
|
108
210
|
behavior.state = BottomSheetBehavior.STATE_HIDDEN
|
|
109
211
|
behavior.halfExpandedRatio = 0.5f
|
|
110
|
-
behavior.addBottomSheetCallback(
|
|
111
|
-
override fun onStateChanged(sheet: View, newState: Int) = onState(newState)
|
|
112
|
-
override fun onSlide(sheet: View, slideOffset: Float) = updateScrim()
|
|
113
|
-
})
|
|
212
|
+
behavior.addBottomSheetCallback(sheetCallback)
|
|
114
213
|
container.viewTreeObserver.addOnGlobalLayoutListener(scrollViews)
|
|
115
214
|
}
|
|
116
215
|
|
|
117
|
-
/**
|
|
118
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Slides the sheet up to its selected detent; [onShown] once it is there. Not [animated], or with the system's
|
|
218
|
+
* animations off ([SystemAnimations]), it stands there at once.
|
|
219
|
+
*/
|
|
220
|
+
fun show(sheetOptions: SheetOptions, swipeToDismiss: Boolean, animated: Boolean, onShown: () -> Unit) {
|
|
119
221
|
topGap = ViewCompat.getRootWindowInsets(layout)?.getInsets(WindowInsetsCompat.Type.systemBars())?.top ?: 0
|
|
120
222
|
layout.addView(scrim, CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
|
|
121
223
|
// As tall as its largest detent (maxHeight), not moved down by a margin: the behavior counts its states from
|
|
122
224
|
// where the sheet is laid out.
|
|
123
|
-
|
|
225
|
+
// In a wide window it stands in the middle, as wide as a Material modal sheet at most.
|
|
226
|
+
behavior.maxWidth = MAX_WIDTH_DP.dp
|
|
227
|
+
val params = CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
|
228
|
+
params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
|
|
229
|
+
params.behavior = behavior
|
|
230
|
+
layout.addView(container, params)
|
|
231
|
+
layout.addOnLayoutChangeListener(layoutHeight)
|
|
124
232
|
shown = onShown
|
|
125
|
-
update(sheetOptions, swipeToDismiss,
|
|
233
|
+
update(sheetOptions, swipeToDismiss, merge = null)
|
|
234
|
+
selected = sheetOptions.selectedDetent?.takeIf { it in detents }
|
|
235
|
+
awaiting = SheetDetent.Content in detents && contentHeight() == null
|
|
236
|
+
if (!animated || SystemAnimations.areOff(context)) {
|
|
237
|
+
// The behavior lays the sheet out at the state it has before its first layout, with no slide, and tells
|
|
238
|
+
// no state then: the sheet stands at its detent once laid out.
|
|
239
|
+
behavior.state = stateOf(showTarget())
|
|
240
|
+
container.post { if (closing == null) onState(behavior.state) }
|
|
241
|
+
return
|
|
242
|
+
}
|
|
126
243
|
// Laid out hidden first, so it slides up from the bottom.
|
|
127
|
-
container.post { if (closing == null) behavior.state = stateOf(
|
|
244
|
+
container.post { if (closing == null) behavior.state = stateOf(showTarget()) }
|
|
128
245
|
}
|
|
129
246
|
|
|
130
|
-
|
|
131
|
-
fun
|
|
132
|
-
|
|
247
|
+
// The stop the show goes to: the selected one when it is a stop already, else the lowest.
|
|
248
|
+
private fun showTarget(): SheetDetent = selected?.takeIf { it in stops } ?: stops.first()
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* New resolved options: the detents and the look. The sheet moves only as [SheetStops] says, after a `mergeOptions`
|
|
252
|
+
* of [merge]; without one (the shown screen changed, the show) it stays where it is.
|
|
253
|
+
*/
|
|
254
|
+
fun update(sheetOptions: SheetOptions, swipeToDismiss: Boolean, merge: SheetOptions?) {
|
|
255
|
+
// The stop it rests at, by the stops it has now: the new ones may give its state another detent.
|
|
256
|
+
val settled = detentOf(behavior.state)
|
|
133
257
|
options = sheetOptions
|
|
134
258
|
this.swipeToDismiss = swipeToDismiss
|
|
135
|
-
detents = sheetOptions.detents?.takeIf { it.isNotEmpty() }
|
|
136
|
-
selected = sheetOptions.selectedDetent?.takeIf { it in detents }
|
|
259
|
+
detents = sheetOptions.detents?.takeIf { it.isNotEmpty() } ?: listOf(SheetDetent.Large)
|
|
137
260
|
grabber.visibility = if (sheetOptions.grabber.isTrue) View.VISIBLE else View.GONE
|
|
138
261
|
behavior.dragAnywhere = sheetOptions.expandsOnScroll.get(true)
|
|
139
262
|
container.invalidateOutline()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (settled != null) behavior.isHideable = swipeToDismiss && settled == detents.first()
|
|
144
|
-
val target = when {
|
|
145
|
-
selected != null && selected != previous -> selected
|
|
146
|
-
settled != null && settled !in detents -> selected ?: detents.first()
|
|
147
|
-
else -> null
|
|
148
|
-
}
|
|
149
|
-
if (target != null) behavior.state = stateOf(target)
|
|
150
|
-
updateScrim()
|
|
263
|
+
hearContent()
|
|
264
|
+
followContent()
|
|
265
|
+
restop(settled, merge?.selectedDetent)
|
|
151
266
|
}
|
|
152
267
|
|
|
153
|
-
/** Slides the sheet down; [onClosed] once it is out of sight. */
|
|
154
|
-
fun close(onClosed: () -> Unit) {
|
|
268
|
+
/** Slides the sheet down; [onClosed] once it is out of sight. Not [animated], or with animations off, at once. */
|
|
269
|
+
fun close(animated: Boolean, onClosed: () -> Unit) {
|
|
155
270
|
closing = onClosed
|
|
156
|
-
if (isClosed) {
|
|
271
|
+
if (isClosed || !animated || SystemAnimations.areOff(context)) {
|
|
272
|
+
stopBackPreview()
|
|
157
273
|
finishClosing()
|
|
158
274
|
return
|
|
159
275
|
}
|
|
276
|
+
// Released by back, it goes down already: it is closed at the end of that slide.
|
|
277
|
+
if (backFinishing) return
|
|
278
|
+
stopBackPreview()
|
|
160
279
|
behavior.isHideable = true
|
|
161
280
|
behavior.state = BottomSheetBehavior.STATE_HIDDEN
|
|
162
281
|
}
|
|
163
282
|
|
|
164
283
|
/** Takes the sheet's views away; the modal's view is the caller's. */
|
|
165
284
|
fun remove() {
|
|
285
|
+
// A slide down of back's release that ends after this tells nothing: the modal went already.
|
|
286
|
+
behavior.removeBottomSheetCallback(sheetCallback)
|
|
287
|
+
stopBackPreview()
|
|
288
|
+
stopBackFrames()
|
|
166
289
|
container.viewTreeObserver.removeOnGlobalLayoutListener(scrollViews)
|
|
290
|
+
layout.removeOnLayoutChangeListener(layoutHeight)
|
|
291
|
+
followed?.setOnContentMounted(CONTENT_KEY, null)
|
|
292
|
+
followed = null
|
|
293
|
+
container.removeCallbacks(measureLater)
|
|
294
|
+
heard?.setOnContentMounted(SCROLL_KEY, null)
|
|
295
|
+
heard = null
|
|
167
296
|
container.removeView(content)
|
|
168
297
|
layout.removeView(container)
|
|
169
298
|
layout.removeView(scrim)
|
|
@@ -176,58 +305,278 @@ internal class ModalSheet(
|
|
|
176
305
|
TestIds.apply(scrim, testId?.let { "$it.scrim" })
|
|
177
306
|
}
|
|
178
307
|
|
|
308
|
+
override val hidesOnBack: Boolean
|
|
309
|
+
get() = closing == null && !backFinishing && behavior.isHideable && container.isLaidOut &&
|
|
310
|
+
detentOf(behavior.state) != null
|
|
311
|
+
|
|
312
|
+
override fun startBackPreview(event: BackEventCompat): SheetBack.Gesture? {
|
|
313
|
+
if (!hidesOnBack || backPreview != null) return null
|
|
314
|
+
val preview = BackPreview()
|
|
315
|
+
backPreview = preview
|
|
316
|
+
startBackFrames()
|
|
317
|
+
behavior.startBackProgress(event)
|
|
318
|
+
return preview
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// A command closed or moved the sheet, or it goes, while back's preview runs: the sheet grows back where it stands
|
|
322
|
+
// (the behavior's cancel), and the gesture's release does the usual back.
|
|
323
|
+
private fun stopBackPreview() {
|
|
324
|
+
if (backPreview == null) return
|
|
325
|
+
backPreview = null
|
|
326
|
+
behavior.cancelBackProgress()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private fun startBackFrames() {
|
|
330
|
+
if (backFramesObserver != null) return
|
|
331
|
+
backFramesObserver = container.viewTreeObserver.also { it.addOnPreDrawListener(backFrames) }
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// The listener leaves the observer it joined: a container out of the window has another one.
|
|
335
|
+
private fun stopBackFrames() {
|
|
336
|
+
val observer = backFramesObserver ?: return
|
|
337
|
+
backFramesObserver = null
|
|
338
|
+
if (observer.isAlive) observer.removeOnPreDrawListener(backFrames)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** The back gesture over this sheet: the Material behavior previews its hide (`MaterialBackHandler`). */
|
|
342
|
+
private inner class BackPreview : SheetBack.Gesture {
|
|
343
|
+
override val sheet: SheetBack
|
|
344
|
+
get() = this@ModalSheet
|
|
345
|
+
|
|
346
|
+
override fun progress(event: BackEventCompat) {
|
|
347
|
+
if (backPreview !== this) return
|
|
348
|
+
behavior.updateBackProgress(event)
|
|
349
|
+
updateScrim()
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
override fun cancel() {
|
|
353
|
+
if (backPreview === this) stopBackPreview()
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// The behavior slides the sheet down from the preview and sets its hidden state at the end: `onState` closes
|
|
357
|
+
// it there as a drag down does (`onClosedByUser`), so the modal goes through the user's path once.
|
|
358
|
+
override fun commit(): Boolean {
|
|
359
|
+
if (backPreview !== this) return false
|
|
360
|
+
backPreview = null
|
|
361
|
+
if (!hidesOnBack) {
|
|
362
|
+
behavior.cancelBackProgress()
|
|
363
|
+
return false
|
|
364
|
+
}
|
|
365
|
+
backFinishing = true
|
|
366
|
+
behavior.handleBackInvoked()
|
|
367
|
+
return true
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
179
371
|
private fun closeByUser() {
|
|
180
372
|
if (!swipeToDismiss) {
|
|
181
373
|
events.onCloseAttempted()
|
|
182
374
|
return
|
|
183
375
|
}
|
|
376
|
+
// With animations off the modal goes at once, and its close takes the sheet away with no slide.
|
|
377
|
+
if (SystemAnimations.areOff(context)) {
|
|
378
|
+
if (closing != null) return
|
|
379
|
+
closing = {}
|
|
380
|
+
return events.onClosedByUser()
|
|
381
|
+
}
|
|
184
382
|
behavior.isHideable = true
|
|
185
383
|
behavior.state = BottomSheetBehavior.STATE_HIDDEN
|
|
186
384
|
}
|
|
187
385
|
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
val
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
386
|
+
// The stops of the detents and how the Material behavior rests at them (`SheetStops.material`): at one or two it
|
|
387
|
+
// fits the sheet to its highest stop, at three it stops half expanded between them. The detent the options select
|
|
388
|
+
// stays the middle one when there are more.
|
|
389
|
+
// A new height of the sheet (`maxHeight`), of its collapsed state, or a change of the mode is laid out: the
|
|
390
|
+
// behavior measures the sheet and counts a fitted sheet's top in its layout. It says whether a layout comes.
|
|
391
|
+
private fun configure(): Boolean {
|
|
392
|
+
appliedContentHeight = contentHeight()
|
|
393
|
+
// A `content` detent not measured yet has no height: it stays out of the stops until it has one, or it would
|
|
394
|
+
// count as the full height and take the place of `large`.
|
|
395
|
+
val measured = detents.filter { it != SheetDetent.Content || contentHeight() != null }.ifEmpty { detents }
|
|
396
|
+
stops = SheetStops.stops(measured, ::heightOf, options.selectedDetent)
|
|
397
|
+
val lift = if (adjustsForKeyboard()) keyboard else 0
|
|
398
|
+
stopHeights = SheetStops.lifted(stops.map(::heightOf), lift, fullHeight())
|
|
399
|
+
val material = SheetStops.material(stopHeights, parentHeight())
|
|
400
|
+
val relayout = behavior.maxHeight != material.maxHeight || behavior.peekHeight != material.peekHeight ||
|
|
401
|
+
behavior.isFitToContents != material.fitToContents
|
|
402
|
+
behavior.isFitToContents = material.fitToContents
|
|
403
|
+
material.halfExpandedRatio?.takeIf { it > 0f && it < 1f }?.let { behavior.halfExpandedRatio = it }
|
|
404
|
+
behavior.expandedOffset = material.expandedOffset
|
|
405
|
+
behavior.maxHeight = material.maxHeight
|
|
406
|
+
behavior.skipCollapsed = material.skipCollapsed
|
|
407
|
+
behavior.peekHeight = material.peekHeight
|
|
408
|
+
if (relayout) container.requestLayout()
|
|
409
|
+
return relayout
|
|
202
410
|
}
|
|
203
411
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
412
|
+
/**
|
|
413
|
+
* The stops or their heights changed (the options, the layout, the content, the keyboard): the behavior takes them,
|
|
414
|
+
* and a sheet at rest goes to [arrived] (a `selectedDetent` a merge names) when it is a stop, else stays at the
|
|
415
|
+
* stop it rested at ([settled], by the stops before the change) where it stands now, else goes to the selected one
|
|
416
|
+
* or the lowest (`SheetStops.target`). A sheet on the move takes them once it rests.
|
|
417
|
+
*/
|
|
418
|
+
private fun restop(settled: SheetDetent? = detentOf(behavior.state), arrived: SheetDetent? = null) {
|
|
419
|
+
if (container.parent == null) return
|
|
420
|
+
val relayout = configure()
|
|
421
|
+
updateScrim()
|
|
422
|
+
if (closing != null || backFinishing) return
|
|
423
|
+
if (isMoving()) {
|
|
424
|
+
restopPending = true
|
|
425
|
+
if (arrived != null) pendingArrived = arrived
|
|
426
|
+
return
|
|
427
|
+
}
|
|
428
|
+
// Not shown yet: the show slides it to the stop a merge names.
|
|
429
|
+
if (settled == null) {
|
|
430
|
+
if (arrived != null && arrived in stops) selected = arrived
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
val shows = awaiting && contentHeight() != null
|
|
434
|
+
if (shows) awaiting = false
|
|
435
|
+
val wanted = arrived ?: if (shows) showTarget() else null
|
|
436
|
+
val target = SheetStops.target(settled, stops, wanted, options.selectedDetent) ?: settled
|
|
437
|
+
behavior.isHideable = swipeToDismiss && target == stops.first()
|
|
438
|
+
// A move, or a sheet that can no longer go down, ends a preview of back on it.
|
|
439
|
+
if (target != settled || !behavior.isHideable) stopBackPreview()
|
|
440
|
+
if (backPreview != null) return
|
|
441
|
+
if (target != settled) selected = target
|
|
442
|
+
if (!relayout) {
|
|
443
|
+
behavior.state = stateOf(target)
|
|
444
|
+
return
|
|
445
|
+
}
|
|
446
|
+
// The new height comes with the sheet's next layout, where the behavior puts a sheet at rest at its state's
|
|
447
|
+
// offset at once; a move to another stop follows it.
|
|
448
|
+
afterNextLayout {
|
|
449
|
+
val gone = closing != null || backFinishing || container.parent == null
|
|
450
|
+
if (gone || backPreview != null) return@afterNextLayout
|
|
451
|
+
if (isMoving()) {
|
|
452
|
+
restopPending = true
|
|
453
|
+
if (target != settled) pendingArrived = target
|
|
454
|
+
return@afterNextLayout
|
|
455
|
+
}
|
|
456
|
+
behavior.state = stateOf(target)
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private fun isMoving(): Boolean =
|
|
461
|
+
behavior.state == BottomSheetBehavior.STATE_DRAGGING || behavior.state == BottomSheetBehavior.STATE_SETTLING
|
|
462
|
+
|
|
463
|
+
// Runs [step] on the pre-draw of the frame of the sheet's next layout: the behavior has placed it by then.
|
|
464
|
+
private fun afterNextLayout(step: () -> Unit) {
|
|
465
|
+
container.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
|
|
466
|
+
override fun onLayoutChange(
|
|
467
|
+
view: View, left: Int, top: Int, right: Int, bottom: Int,
|
|
468
|
+
oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int,
|
|
469
|
+
) {
|
|
470
|
+
view.removeOnLayoutChangeListener(this)
|
|
471
|
+
OneShotPreDrawListener.add(view, step)
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private fun stateOf(detent: SheetDetent): Int = when (detent) {
|
|
477
|
+
stops.last() -> BottomSheetBehavior.STATE_EXPANDED
|
|
478
|
+
stops.first() -> BottomSheetBehavior.STATE_COLLAPSED
|
|
479
|
+
else -> BottomSheetBehavior.STATE_HALF_EXPANDED
|
|
208
480
|
}
|
|
209
481
|
|
|
210
482
|
private fun detentOf(state: Int): SheetDetent? = when (state) {
|
|
211
|
-
BottomSheetBehavior.STATE_EXPANDED ->
|
|
212
|
-
BottomSheetBehavior.STATE_HALF_EXPANDED ->
|
|
213
|
-
BottomSheetBehavior.STATE_COLLAPSED ->
|
|
483
|
+
BottomSheetBehavior.STATE_EXPANDED -> stops.last()
|
|
484
|
+
BottomSheetBehavior.STATE_HALF_EXPANDED -> stops.getOrNull(1)?.takeIf { stops.size == SheetStops.MAX_STOPS }
|
|
485
|
+
BottomSheetBehavior.STATE_COLLAPSED -> stops.first()
|
|
214
486
|
else -> null
|
|
215
487
|
}
|
|
216
488
|
|
|
489
|
+
private fun parentHeight(): Int = if (layout.height > 0) layout.height else layout.rootView.height
|
|
490
|
+
|
|
491
|
+
// The tallest a sheet is: the layout under the status bar.
|
|
492
|
+
private fun fullHeight(): Int = parentHeight() - topGap
|
|
493
|
+
|
|
217
494
|
/** The height of the sheet at the detent, in px. */
|
|
218
495
|
private fun heightOf(detent: SheetDetent): Int {
|
|
219
|
-
val full = (
|
|
496
|
+
val full = fullHeight()
|
|
220
497
|
return when (detent) {
|
|
221
498
|
SheetDetent.Large -> full
|
|
222
499
|
SheetDetent.Medium -> (full + topGap) / 2
|
|
500
|
+
SheetDetent.Content -> contentHeight()?.coerceAtMost(full) ?: full
|
|
223
501
|
is SheetDetent.Height -> detent.dp.toFloat().dp.coerceAtMost(full)
|
|
224
502
|
}
|
|
225
503
|
}
|
|
226
504
|
|
|
505
|
+
// The `content` detent follows the content of the screen shown now, and measures it after each of its mounts; a
|
|
506
|
+
// sheet without the detent follows nothing.
|
|
507
|
+
private fun followContent() {
|
|
508
|
+
val content = if (SheetDetent.Content in detents) shownContent() else null
|
|
509
|
+
if (content !== followed) {
|
|
510
|
+
followed?.setOnContentMounted(CONTENT_KEY, null)
|
|
511
|
+
followed = content
|
|
512
|
+
// The last height stays until the new content has one: a push in the sheet doesn't jump it to the top.
|
|
513
|
+
content?.setOnContentMounted(CONTENT_KEY) { contentMounted() }
|
|
514
|
+
}
|
|
515
|
+
measureContent()
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Fabric tells every mount of the app: the content is measured at most once per [MEASURE_INTERVAL_MS], the last
|
|
519
|
+
// mount of a burst included, so a screen that renders each frame elsewhere doesn't lay the sheet's tree out each
|
|
520
|
+
// frame too.
|
|
521
|
+
private fun contentMounted() {
|
|
522
|
+
val wait = lastMeasure + MEASURE_INTERVAL_MS - SystemClock.uptimeMillis()
|
|
523
|
+
if (wait > 0) {
|
|
524
|
+
if (!measureWaits) {
|
|
525
|
+
measureWaits = true
|
|
526
|
+
container.postDelayed(measureLater, wait)
|
|
527
|
+
}
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
measureLater.run()
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// The content's own height at the width it has (the sheet's, before it is laid out); kept until a measure gives
|
|
534
|
+
// one again.
|
|
535
|
+
private fun measureContent() {
|
|
536
|
+
val content = followed ?: return
|
|
537
|
+
val width = content.asView().width.takeIf { it > 0 } ?: container.width.takeIf { it > 0 } ?: sheetWidth()
|
|
538
|
+
content.contentSize(width)?.let { contentBody = it.height }
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// The height of the `content` detent: the content's own, the bars above it in the sheet (a stack's top bar), and
|
|
542
|
+
// the navigation bar below it, which a detent leaves room for as UIKit adds the bottom safe area; none before the
|
|
543
|
+
// content is measured.
|
|
544
|
+
private fun contentHeight(): Int? {
|
|
545
|
+
val body = contentBody ?: return null
|
|
546
|
+
val insets = ViewCompat.getRootWindowInsets(layout)
|
|
547
|
+
val bottom = insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
|
|
548
|
+
return offsetAbove() + body + bottom
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// How far below the sheet's top the content stands, by layout: the views between them, without their transforms.
|
|
552
|
+
private fun offsetAbove(): Int {
|
|
553
|
+
var view: View = followed?.asView() ?: return 0
|
|
554
|
+
var top = 0
|
|
555
|
+
while (view !== container) {
|
|
556
|
+
top += view.top
|
|
557
|
+
view = view.parent as? View ?: return 0
|
|
558
|
+
}
|
|
559
|
+
return top
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// The content may have changed its height: the stops follow it.
|
|
563
|
+
private fun contentChanged() {
|
|
564
|
+
if (contentHeight() != appliedContentHeight) restop()
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private fun sheetWidth(): Int {
|
|
568
|
+
val width = if (layout.width > 0) layout.width else layout.rootView.width
|
|
569
|
+
return width.coerceAtMost(MAX_WIDTH_DP.dp)
|
|
570
|
+
}
|
|
571
|
+
|
|
227
572
|
private fun onState(state: Int) {
|
|
228
573
|
when (state) {
|
|
229
|
-
BottomSheetBehavior.STATE_DRAGGING ->
|
|
574
|
+
BottomSheetBehavior.STATE_DRAGGING -> {
|
|
575
|
+
dragged = true
|
|
576
|
+
awaiting = false
|
|
577
|
+
}
|
|
230
578
|
BottomSheetBehavior.STATE_HIDDEN -> {
|
|
579
|
+
backFinishing = false
|
|
231
580
|
when {
|
|
232
581
|
closing != null -> finishClosing()
|
|
233
582
|
else -> {
|
|
@@ -241,7 +590,7 @@ internal class ModalSheet(
|
|
|
241
590
|
if (closing != null) return
|
|
242
591
|
val detent = detentOf(state)
|
|
243
592
|
// A drag down closes the sheet from its smallest detent only; from a larger one it stops at the next.
|
|
244
|
-
behavior.isHideable = swipeToDismiss && detent ==
|
|
593
|
+
behavior.isHideable = swipeToDismiss && detent == stops.first()
|
|
245
594
|
shown?.let {
|
|
246
595
|
shown = null
|
|
247
596
|
it()
|
|
@@ -251,6 +600,12 @@ internal class ModalSheet(
|
|
|
251
600
|
events.onDetentChanged(detent)
|
|
252
601
|
}
|
|
253
602
|
dragged = false
|
|
603
|
+
if (restopPending) {
|
|
604
|
+
val arrived = pendingArrived
|
|
605
|
+
restopPending = false
|
|
606
|
+
pendingArrived = null
|
|
607
|
+
container.post { restop(arrived = arrived) }
|
|
608
|
+
}
|
|
254
609
|
}
|
|
255
610
|
}
|
|
256
611
|
updateScrim()
|
|
@@ -265,13 +620,23 @@ internal class ModalSheet(
|
|
|
265
620
|
// The screen under the sheet dims as the sheet goes up past `largestUndimmedDetent` (from the bottom without
|
|
266
621
|
// one), to the dim of a Material modal sheet at the next detent; while it isn't dimmed, it takes touches.
|
|
267
622
|
private fun updateScrim() {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
val
|
|
271
|
-
val
|
|
623
|
+
// Hidden at the end of back's slide, it still stands at its detent until the next layout.
|
|
624
|
+
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) return setDim(0f)
|
|
625
|
+
val height = layout.height - sheetTop()
|
|
626
|
+
val undimmed = stops.indexOf(options.largestUndimmedDetent).takeIf { it >= 0 }
|
|
627
|
+
val from = undimmed?.let { stopHeights.getOrNull(it) } ?: 0
|
|
628
|
+
val to = stopHeights.firstOrNull { it > from } ?: return run { setDim(0f) }
|
|
272
629
|
setDim(((height - from).toFloat() / (to - from)).coerceIn(0f, 1f))
|
|
273
630
|
}
|
|
274
631
|
|
|
632
|
+
// The top of the sheet as drawn: its offset, and while back's preview moves it, its slide and its scale toward
|
|
633
|
+
// the bottom (the behavior's pivot).
|
|
634
|
+
private fun sheetTop(): Int {
|
|
635
|
+
if (backFramesObserver == null) return container.top
|
|
636
|
+
val scaled = container.pivotY * (1f - container.scaleY)
|
|
637
|
+
return (container.top + container.translationY + scaled).roundToInt()
|
|
638
|
+
}
|
|
639
|
+
|
|
275
640
|
private fun setDim(fraction: Float) {
|
|
276
641
|
scrim.alpha = fraction * SCRIM_ALPHA
|
|
277
642
|
}
|
|
@@ -280,9 +645,35 @@ internal class ModalSheet(
|
|
|
280
645
|
|
|
281
646
|
private fun cornerRadius(): Int = if (options.cornerRadius.hasValue()) options.cornerRadius.get().toFloat().dp else 28.dp
|
|
282
647
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
648
|
+
// React Native mounts the lists of the shown screen after the sheet's layout, and its views take no layout of their
|
|
649
|
+
// own: the sheet hears each mount of the content and turns a new list to scroll nested there.
|
|
650
|
+
private fun hearContent() {
|
|
651
|
+
val content = if (behavior.dragAnywhere) shownContent() else null
|
|
652
|
+
if (content !== heard) {
|
|
653
|
+
heard?.setOnContentMounted(SCROLL_KEY, null)
|
|
654
|
+
heard = content
|
|
655
|
+
content?.setOnContentMounted(SCROLL_KEY) { turnScrollViews() }
|
|
656
|
+
}
|
|
657
|
+
turnScrollViews()
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// The behavior finds the lists that scroll nested only in its layout of the sheet: a list turned so gets the sheet
|
|
661
|
+
// one more, else the behavior knows no list and takes a drag down on a scrolled one for a drag of the sheet.
|
|
662
|
+
private fun turnScrollViews() {
|
|
663
|
+
if (behavior.dragAnywhere && enableNestedScrolling(container)) container.requestLayout()
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Whether a list was turned to scroll nested now.
|
|
667
|
+
private fun enableNestedScrolling(view: View): Boolean {
|
|
668
|
+
var turned = false
|
|
669
|
+
if (view is ScrollView && !view.isNestedScrollingEnabled) {
|
|
670
|
+
view.isNestedScrollingEnabled = true
|
|
671
|
+
turned = true
|
|
672
|
+
}
|
|
673
|
+
if (view is ViewGroup) {
|
|
674
|
+
for (i in 0 until view.childCount) turned = enableNestedScrolling(view.getChildAt(i)) || turned
|
|
675
|
+
}
|
|
676
|
+
return turned
|
|
286
677
|
}
|
|
287
678
|
|
|
288
679
|
/** With `expandsOnScroll: false` only the top of the sheet, around the grabber, drags it; the rest scrolls. */
|
|
@@ -298,7 +689,7 @@ internal class ModalSheet(
|
|
|
298
689
|
when (event.actionMasked) {
|
|
299
690
|
MotionEvent.ACTION_DOWN -> {
|
|
300
691
|
downY = event.rawY
|
|
301
|
-
downAtSmallest = detentOf(state) ==
|
|
692
|
+
downAtSmallest = detentOf(state) == stops.first()
|
|
302
693
|
}
|
|
303
694
|
MotionEvent.ACTION_UP -> {
|
|
304
695
|
if (!swipeToDismiss && closing == null && downAtSmallest && event.rawY - downY > ATTEMPT_DISTANCE_DP.dp) {
|
|
@@ -333,10 +724,22 @@ internal class ModalSheet(
|
|
|
333
724
|
}
|
|
334
725
|
}
|
|
335
726
|
|
|
336
|
-
|
|
727
|
+
companion object {
|
|
728
|
+
/** The widest a sheet is, in dp: the Material modal bottom sheet's `maxWidth`. */
|
|
729
|
+
const val MAX_WIDTH_DP = 640
|
|
730
|
+
|
|
337
731
|
/** The dim of a Material modal bottom sheet's scrim. */
|
|
338
|
-
const val SCRIM_ALPHA = 0.32f
|
|
339
|
-
const val GRAB_AREA_DP = 48
|
|
340
|
-
const val ATTEMPT_DISTANCE_DP = 48
|
|
732
|
+
private const val SCRIM_ALPHA = 0.32f
|
|
733
|
+
private const val GRAB_AREA_DP = 48
|
|
734
|
+
private const val ATTEMPT_DISTANCE_DP = 48
|
|
735
|
+
|
|
736
|
+
// The key of the sheet's handler of its content's mounts.
|
|
737
|
+
private const val CONTENT_KEY = "sheet.content"
|
|
738
|
+
|
|
739
|
+
// The least time between two measures of the content after its mounts.
|
|
740
|
+
private const val MEASURE_INTERVAL_MS = 100L
|
|
741
|
+
|
|
742
|
+
// The key of the sheet's handler that turns its content's lists to scroll nested.
|
|
743
|
+
private const val SCROLL_KEY = "sheet.scroll"
|
|
341
744
|
}
|
|
342
745
|
}
|