@apexnative/navigation 1.0.0-alpha.5 → 1.0.0-alpha.7
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 +32 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +31 -20
- package/android/CORE.md +12 -8
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
- 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/ButtonOptions.kt +1 -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/SheetOptions.kt +7 -4
- 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/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/NavigationTurboModule.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- 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 +9 -7
- 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 +13 -10
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
- 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/ModalCoordinator.kt +20 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +443 -58
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +33 -5
- 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 +60 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
- 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 +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +50 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +10 -4
- 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 +215 -43
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
- 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/TopBarPresenter.kt +36 -38
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +129 -179
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +67 -3
- 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 +55 -42
- 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/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 -24
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -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 +121 -175
- 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/doctor/checks/app.js +16 -1
- package/cli/project.js +4 -0
- package/cli/setup/index.js +5 -0
- package/cli/setup/steps/metro.js +46 -0
- 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 +46 -5
- package/docs-md/containers/modals.md +2 -0
- package/docs-md/containers/overlays.md +1 -1
- package/docs-md/containers/side-menu.md +1 -1
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/bottom-sheets.md +4 -2
- 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 +3 -1
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +22 -1
- package/docs-md/index.json +41 -1
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +1 -1
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +17 -1
- 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 +16 -11
- 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 +16 -1
- 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 +49 -3
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- package/ios/Animations/Elements/ANNElementFinder.h +2 -2
- package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
- package/ios/Animations/Elements/ANNElementTransition.h +3 -3
- package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
- package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +249 -45
- 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 +32 -22
- package/ios/CORE.md +25 -21
- package/ios/Contract/ANNCommandRunner.h +8 -7
- package/ios/Contract/ANNCommandRunner.mm +21 -27
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNDeepLinkQueue.h +6 -5
- package/ios/Core/ANNDeepLinkQueue.mm +22 -10
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ANNSceneDelegate.mm +2 -2
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Core/ApexNavigation.mm +6 -2
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
- package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +13 -23
- 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/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +153 -11
- 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/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
- 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/ANNOptionsReader.mm +108 -0
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -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 +4 -0
- package/ios/Options/Params/Image.mm +20 -0
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
- package/ios/React/ANNSurfaceHostingView.h +14 -0
- package/ios/React/ANNSurfaceHostingView.mm +144 -1
- 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 +5 -3
- package/ios/TopBar/ANNBarButtons.mm +186 -17
- package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
- 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 +2 -2
- package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
- package/ios/TopBar/ANNTopBarPresenter.h +3 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
- package/ios/TurboModules/ANNTurboModule.mm +66 -16
- 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 +17 -7
- 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 +188 -66
- package/lib/module/api/app/createNavigation.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/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- 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 +172 -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 +42 -2
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +7 -2
- 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 +5 -4
- 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/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +60 -0
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +55 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
- 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/events/navigationEvents.d.ts +2 -2
- package/lib/typescript/api/events/navigationEvents.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/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- 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/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- 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 +2 -2
- package/lib/typescript/interfaces/options/animations.d.ts +44 -14
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +19 -6
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +10 -10
- package/lib/typescript/interfaces/options/modals.d.ts +6 -7
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +8 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +8 -5
- 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 +46 -29
- 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 +17 -7
- package/src/adapters/NativeANNTurboModule.ts +7 -0
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +201 -69
- package/src/api/events/navigationEvents.ts +9 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +170 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +51 -6
- package/src/engine/layout.ts +11 -2
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +6 -8
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +70 -0
- package/src/engine/screens.tsx +53 -8
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/options/animations.ts +44 -14
- package/src/interfaces/options/bottomTabs.ts +19 -6
- package/src/interfaces/options/buttons.ts +10 -10
- package/src/interfaces/options/modals.ts +6 -7
- package/src/interfaces/options/stack.ts +8 -3
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +46 -29
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
- 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 -136
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -86
- 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 -64
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -177
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -137
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
@implementation ANNTabsDesign
|
|
4
4
|
|
|
5
|
-
// Read once: the Info.plist of a running app doesn't change.
|
|
5
|
+
// Read once: the Info.plist of a running app doesn't change. iOS 26 keeps the earlier design for the key; iOS 27 keeps
|
|
6
|
+
// it only for an app built with an SDK before iOS 27 and ignores it for one built with the iOS 27 SDK (an app linked
|
|
7
|
+
// against the iOS 26 SDK keeps the earlier design on iOS 27, one linked against the iOS 27 SDK gets the glass). The
|
|
8
|
+
// core is compiled with the app, so the SDK it is compiled with is the app's.
|
|
6
9
|
+ (BOOL)isCompatibilityMode {
|
|
7
10
|
static BOOL compatibility;
|
|
8
11
|
static dispatch_once_t once;
|
|
@@ -11,6 +14,11 @@
|
|
|
11
14
|
id value = NSBundle.mainBundle.infoDictionary[@"UIDesignRequiresCompatibility"];
|
|
12
15
|
compatibility = [value respondsToSelector:@selector(boolValue)] && [value boolValue];
|
|
13
16
|
}
|
|
17
|
+
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 270000
|
|
18
|
+
if (@available(iOS 27.0, *)) {
|
|
19
|
+
compatibility = NO;
|
|
20
|
+
}
|
|
21
|
+
#endif
|
|
14
22
|
});
|
|
15
23
|
return compatibility;
|
|
16
24
|
}
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The button of an item in the tab bar. UIKit has no public API for it: this reads the private
|
|
7
|
-
* `view` of the item,
|
|
7
|
+
* `view` of the item, the core's one private read of the bar, and is nil when the item has no
|
|
8
|
+
* button (out of the bar, or before its layout). When UIKit has no such key any more, DEBUG logs it
|
|
9
|
+
* once.
|
|
8
10
|
*/
|
|
9
11
|
- (UIView *)viewOfItem:(UITabBarItem *)item;
|
|
10
12
|
|
|
11
|
-
- (UIView *)tabBarItemViewAtIndex:(NSUInteger)index;
|
|
12
|
-
|
|
13
13
|
- (void)removeTabBarItemTitles;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Icons in the middle of the buttons of this tab bar, for items without titles: before iOS 26 (and
|
|
17
|
-
* in its compatibility mode) a button keeps the room of its title; the iOS 26
|
|
18
|
-
* icons itself, and this does nothing there. UIKit has no public API for it: the layout of the
|
|
17
|
+
* in its compatibility mode, `ANNTabsDesign`) a button keeps the room of its title; the iOS 26
|
|
18
|
+
* design centers the icons itself, and this does nothing there. UIKit has no public API for it: the layout of the
|
|
19
19
|
* private button class changes once for the app, and only tab bars with centered images move their
|
|
20
20
|
* icons (and badges). `NO` gives the buttons the layout of UIKit back.
|
|
21
21
|
*/
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#import "UITabBar+utils.h"
|
|
2
|
+
#import "ANNTabsDesign.h"
|
|
2
3
|
#import "UIView+Utils.h"
|
|
4
|
+
#import <React/RCTLog.h>
|
|
3
5
|
#import <objc/runtime.h>
|
|
4
6
|
|
|
5
7
|
static const void *ANNItemImagesCenteredKey = &ANNItemImagesCenteredKey;
|
|
@@ -37,17 +39,16 @@ static void ANNTabBarButtonLayoutSubviews(UIView *button, SEL _cmd) {
|
|
|
37
39
|
badge.center = CGPointMake(badge.center.x, badge.center.y + offset);
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
// The design of iOS 26 centers the icon of an item without a title itself;
|
|
41
|
-
//
|
|
42
|
+
// The design of iOS 26 centers the icon of an item without a title itself; the design before it
|
|
43
|
+
// (before iOS 26, and the compatibility mode where UIKit keeps it) keeps the title's room.
|
|
42
44
|
static BOOL ANNTabBarButtonsKeepTitleRoom(void) {
|
|
43
|
-
|
|
44
|
-
return [[NSBundle.mainBundle objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"]
|
|
45
|
-
boolValue];
|
|
46
|
-
}
|
|
47
|
-
return YES;
|
|
45
|
+
return !ANNTabsDesign.isGlass;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
// Once for the app; the buttons of other tab bars keep the layout of UIKit.
|
|
48
|
+
// Once for the app; the buttons of other tab bars keep the layout of UIKit. No public API does this: in the
|
|
49
|
+
// compatibility mode of iOS 26 UIKit ignores `imageInsets` and a symbol's baseline offset, and a symbol drawn into a
|
|
50
|
+
// taller bitmap is scaled down and stops being a symbol. `badgePositionAdjustment` would move the badge alone, so the
|
|
51
|
+
// button's layout moves both.
|
|
51
52
|
static void ANNInstallTabBarButtonLayout(void) {
|
|
52
53
|
static dispatch_once_t once;
|
|
53
54
|
dispatch_once(&once, ^{
|
|
@@ -65,18 +66,36 @@ static void ANNInstallTabBarButtonLayout(void) {
|
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
// Once per app run, in DEBUG: the tab items' buttons can't be found any more.
|
|
70
|
+
static void ANNWarnNoButtonOfItem(void) {
|
|
71
|
+
#if DEBUG
|
|
72
|
+
static dispatch_once_t once;
|
|
73
|
+
dispatch_once(&once, ^{
|
|
74
|
+
RCTLogWarn(@"[ApexNavigation] This iOS version gives no button for a tab item: "
|
|
75
|
+
@"bottomTab.testID on the tab buttons, the dot before iOS 26 and the long "
|
|
76
|
+
@"press on a tab don't apply.");
|
|
77
|
+
});
|
|
78
|
+
#endif
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
@implementation UITabBar (utils)
|
|
69
82
|
|
|
70
83
|
- (UIView *)viewOfItem:(UITabBarItem *)item {
|
|
84
|
+
if (!item) {
|
|
85
|
+
return nil;
|
|
86
|
+
}
|
|
87
|
+
// An item out of the bar (in More, or before the bar's first layout) has no button: that is no failure. Only an
|
|
88
|
+
// item without the `view` key, or with something else under it, says that UIKit changed.
|
|
71
89
|
if (![item respondsToSelector:NSSelectorFromString(@"view")]) {
|
|
90
|
+
ANNWarnNoButtonOfItem();
|
|
72
91
|
return nil;
|
|
73
92
|
}
|
|
74
93
|
id view = [item valueForKey:@"view"];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return
|
|
94
|
+
if (view && ![view isKindOfClass:UIView.class]) {
|
|
95
|
+
ANNWarnNoButtonOfItem();
|
|
96
|
+
return nil;
|
|
97
|
+
}
|
|
98
|
+
return view;
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
- (void)removeTabBarItemTitles {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#import "ANNOptions.h"
|
|
4
4
|
#import "ANNSetStackRootAnimationOptions.h"
|
|
5
5
|
#import "ANNStackAnimationOptions.h"
|
|
6
|
+
#import "ANNStackPopAnimationOptions.h"
|
|
6
7
|
#import "ANNStackPushAnimationOptions.h"
|
|
7
8
|
|
|
8
9
|
NS_ASSUME_NONNULL_BEGIN
|
|
@@ -11,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
11
12
|
@interface ANNAnimationOptions : ANNOptions
|
|
12
13
|
|
|
13
14
|
@property(nonatomic, strong) ANNStackPushAnimationOptions *push;
|
|
14
|
-
@property(nonatomic, strong)
|
|
15
|
+
@property(nonatomic, strong) ANNStackPopAnimationOptions *pop;
|
|
15
16
|
@property(nonatomic, strong) ANNSetStackRootAnimationOptions *setStackRoot;
|
|
16
17
|
@property(nonatomic, strong) ANNEnterExitScreenAnimationOptions *setRoot;
|
|
17
18
|
@property(nonatomic, strong) ANNModalAnimationOptions *showModal;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
+ (NSArray<ANNOptionField *> *)optionFields {
|
|
6
6
|
return @[
|
|
7
7
|
[ANNOptionField groupField:@"push" class:ANNStackPushAnimationOptions.class],
|
|
8
|
-
[ANNOptionField groupField:@"pop" class:
|
|
8
|
+
[ANNOptionField groupField:@"pop" class:ANNStackPopAnimationOptions.class],
|
|
9
9
|
[ANNOptionField groupField:@"setStackRoot" class:ANNSetStackRootAnimationOptions.class],
|
|
10
10
|
[ANNOptionField groupField:@"setRoot" class:ANNEnterExitScreenAnimationOptions.class],
|
|
11
11
|
[ANNOptionField groupField:@"showModal" class:ANNModalAnimationOptions.class],
|
|
@@ -11,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
11
11
|
|
|
12
12
|
@property(nonatomic, strong) Bool *visible;
|
|
13
13
|
@property(nonatomic, strong) Bool *animate;
|
|
14
|
+
/// `'platform'` or `'none'`; read on Android only: the system switches tabs at once.
|
|
15
|
+
@property(nonatomic, strong) Text *switchAnimation;
|
|
14
16
|
/// The index of the tab to select; a whole number (the fraction is dropped).
|
|
15
17
|
@property(nonatomic, strong) Double *currentTabIndex;
|
|
16
18
|
@property(nonatomic, strong) Text *currentTabId;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
return @[
|
|
7
7
|
[ANNOptionField boolField:@"visible"],
|
|
8
8
|
[ANNOptionField boolField:@"animate"],
|
|
9
|
+
[ANNOptionField enumField:@"switchAnimation" values:@[ @"platform", @"none" ]],
|
|
9
10
|
[ANNOptionField doubleField:@"currentTabIndex"],
|
|
10
11
|
[ANNOptionField textField:@"currentTabId"],
|
|
11
12
|
[ANNOptionField textField:@"testID"],
|
package/ios/Options/ANNOptions.h
CHANGED
|
@@ -20,6 +20,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
20
20
|
/// The group of options under `key`: nil when it is not an object (`null` included), which reads as an empty group.
|
|
21
21
|
NSDictionary *_Nullable ANNOptionsDictionary(NSDictionary *_Nullable dict, NSString *key);
|
|
22
22
|
|
|
23
|
+
/// Two values a group of options holds are the same: params and groups by `isSameValueAs:`, arrays item by item,
|
|
24
|
+
/// anything else as `ANNJSONValuesSame` says. nil is the same only as nil (an unset array is not an empty one).
|
|
25
|
+
BOOL ANNOptionValuesSame(id _Nullable a, id _Nullable b);
|
|
26
|
+
|
|
27
|
+
/// A hash of a value `ANNOptionValuesSame` compares: values that are the same have the same hash.
|
|
28
|
+
NSUInteger ANNOptionValueHash(id _Nullable value);
|
|
29
|
+
|
|
23
30
|
#if DEBUG
|
|
24
31
|
/// Development only: checks the field table of `root` and of every group class reached through its group and
|
|
25
32
|
/// group-array fields.
|
|
@@ -51,12 +58,30 @@ void ANNCheckOptionClassesFrom(Class root);
|
|
|
51
58
|
/// The group has any value.
|
|
52
59
|
- (BOOL)hasValue;
|
|
53
60
|
|
|
61
|
+
/// Takes the value at `path` out, in place: the JSON keys from this group, the last one a value, an array or a whole
|
|
62
|
+
/// group, which gets what a freshly made group has there (unset). A key of a text's font goes through the text. A
|
|
63
|
+
/// module's group (an empty field table) is walked by its properties named as the keys. NO, and nothing changes, when
|
|
64
|
+
/// a key of the path is none of the group's or a key before the last is no group. Only on a copy of own options
|
|
65
|
+
/// (`changeOwnOptions:`), never on options in use.
|
|
66
|
+
- (BOOL)clearValueAtPath:(NSArray<NSString *> *)path;
|
|
67
|
+
|
|
54
68
|
/// A deep copy: a new group of the same class with these merged into it. The copy shares values, never groups.
|
|
55
69
|
- (instancetype)copy;
|
|
56
70
|
|
|
57
71
|
/// These over a copy of `defaults`; neither changes.
|
|
58
72
|
- (instancetype)withDefault:(nullable ANNOptions *)defaults;
|
|
59
73
|
|
|
74
|
+
/// A group of the same class with the same options, field by field at any depth: two reads of the same JSON, a copy,
|
|
75
|
+
/// two resolutions of the same layers. The fields are the field table's; a module's group (an empty table) compares
|
|
76
|
+
/// every property of an options kind it declares. A value that is nil is the same only as nil.
|
|
77
|
+
///
|
|
78
|
+
/// Not `isEqual:`: a group changes in place (a merge, the pick of a menu's item), so a hash by value would move under
|
|
79
|
+
/// a set or a dictionary that holds it. `==` and `isEqual:` stay identity.
|
|
80
|
+
- (BOOL)isSameValueAs:(nullable ANNOptions *)other;
|
|
81
|
+
|
|
82
|
+
/// A hash consistent with `isSameValueAs:`; it changes when the group does.
|
|
83
|
+
- (NSUInteger)valueHash;
|
|
84
|
+
|
|
60
85
|
@end
|
|
61
86
|
|
|
62
87
|
NS_ASSUME_NONNULL_END
|
|
@@ -11,7 +11,6 @@ NSDictionary *ANNOptionsDictionary(NSDictionary *dict, NSString *key) {
|
|
|
11
11
|
return [value isKindOfClass:NSDictionary.class] ? value : nil;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
#if DEBUG
|
|
15
14
|
// The kinds of property a group holds options in.
|
|
16
15
|
static BOOL ANNIsOptionClass(Class cls) {
|
|
17
16
|
return [cls isSubclassOfClass:ANNParam.class] || [cls isSubclassOfClass:ANNOptions.class] ||
|
|
@@ -33,6 +32,7 @@ static Class ANNPropertyClass(objc_property_t property) {
|
|
|
33
32
|
return name ? NSClassFromString(name) : nil;
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
#if DEBUG
|
|
36
36
|
// There is no iOS test target: in development each group of the core checks once that its table names every property
|
|
37
37
|
// that holds options, and that every field of the table has its property (the stand-in for the whole-tree test of
|
|
38
38
|
// Android). A missing field would be neither read, merged, copied nor given defaults.
|
|
@@ -86,6 +86,94 @@ static NSArray<ANNOptionField *> *ANNFieldsOf(Class cls) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
// The properties that hold the options of a group, asked once per class: its field table's, or for a module's group
|
|
90
|
+
// (its own read and merge, an empty table) every property of an options kind its classes declare up to `ANNOptions`.
|
|
91
|
+
static NSArray<NSString *> *ANNValuePropertiesOf(Class cls) {
|
|
92
|
+
static NSMutableDictionary<NSString *, NSArray<NSString *> *> *propertiesByClass;
|
|
93
|
+
NSString *name = NSStringFromClass(cls);
|
|
94
|
+
@synchronized(ANNOptions.class) {
|
|
95
|
+
if (!propertiesByClass) {
|
|
96
|
+
propertiesByClass = [NSMutableDictionary new];
|
|
97
|
+
}
|
|
98
|
+
NSArray<NSString *> *names = propertiesByClass[name];
|
|
99
|
+
if (names) {
|
|
100
|
+
return names;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
NSArray<ANNOptionField *> *fields = ANNFieldsOf(cls);
|
|
104
|
+
NSMutableArray<NSString *> *found = [NSMutableArray new];
|
|
105
|
+
if (fields.count) {
|
|
106
|
+
for (ANNOptionField *field in fields) {
|
|
107
|
+
[found addObject:field.property];
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
NSSet<NSString *> *inherited =
|
|
111
|
+
[NSSet setWithArray:@[ @"description", @"debugDescription" ]];
|
|
112
|
+
for (Class current = cls; current && current != ANNOptions.class;
|
|
113
|
+
current = class_getSuperclass(current)) {
|
|
114
|
+
unsigned int count = 0;
|
|
115
|
+
objc_property_t *properties = class_copyPropertyList(current, &count);
|
|
116
|
+
for (unsigned int i = 0; i < count; i++) {
|
|
117
|
+
NSString *property = @(property_getName(properties[i]));
|
|
118
|
+
Class propertyClass = ANNPropertyClass(properties[i]);
|
|
119
|
+
if (propertyClass && ANNIsOptionClass(propertyClass) &&
|
|
120
|
+
![inherited containsObject:property] && ![found containsObject:property]) {
|
|
121
|
+
[found addObject:property];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
free(properties);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
NSArray<NSString *> *names = [found copy];
|
|
128
|
+
@synchronized(ANNOptions.class) {
|
|
129
|
+
propertiesByClass[name] = names;
|
|
130
|
+
}
|
|
131
|
+
return names;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
BOOL ANNOptionValuesSame(id a, id b) {
|
|
135
|
+
if (a == b) {
|
|
136
|
+
return YES;
|
|
137
|
+
}
|
|
138
|
+
if ([a isKindOfClass:ANNParam.class]) {
|
|
139
|
+
return [b isKindOfClass:ANNParam.class] && [(ANNParam *)a isSameValueAs:b];
|
|
140
|
+
}
|
|
141
|
+
if ([a isKindOfClass:ANNOptions.class]) {
|
|
142
|
+
return [b isKindOfClass:ANNOptions.class] && [(ANNOptions *)a isSameValueAs:b];
|
|
143
|
+
}
|
|
144
|
+
if ([a isKindOfClass:NSArray.class]) {
|
|
145
|
+
if (![b isKindOfClass:NSArray.class] || [a count] != [b count]) {
|
|
146
|
+
return NO;
|
|
147
|
+
}
|
|
148
|
+
NSArray *items = b;
|
|
149
|
+
NSUInteger index = 0;
|
|
150
|
+
for (id item in (NSArray *)a) {
|
|
151
|
+
if (!ANNOptionValuesSame(item, items[index++])) {
|
|
152
|
+
return NO;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return YES;
|
|
156
|
+
}
|
|
157
|
+
return ANNJSONValuesSame(a, b);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
NSUInteger ANNOptionValueHash(id value) {
|
|
161
|
+
if ([value isKindOfClass:ANNParam.class]) {
|
|
162
|
+
return [(ANNParam *)value valueHash];
|
|
163
|
+
}
|
|
164
|
+
if ([value isKindOfClass:ANNOptions.class]) {
|
|
165
|
+
return [(ANNOptions *)value valueHash];
|
|
166
|
+
}
|
|
167
|
+
if ([value isKindOfClass:NSArray.class]) {
|
|
168
|
+
NSUInteger hash = [value count];
|
|
169
|
+
for (id item in value) {
|
|
170
|
+
hash = hash * 31 + ANNOptionValueHash(item);
|
|
171
|
+
}
|
|
172
|
+
return hash;
|
|
173
|
+
}
|
|
174
|
+
return ANNJSONValueHash(value);
|
|
175
|
+
}
|
|
176
|
+
|
|
89
177
|
#if DEBUG
|
|
90
178
|
// Runs the check on `root` and on every class reached through its group and group-array fields, so a class that only
|
|
91
179
|
// an array holds (a button, an action of a fab, a token of the search bar) is checked although no object of it is made.
|
|
@@ -310,6 +398,51 @@ static NSArray *ANNCopyItems(NSArray *items) {
|
|
|
310
398
|
return NO;
|
|
311
399
|
}
|
|
312
400
|
|
|
401
|
+
// Generic over the whole tree, so that any option an edit takes out (`clearOptions`) goes without a list of options:
|
|
402
|
+
// the field table names each key's property; a module's group, which reads its JSON itself, names its properties as
|
|
403
|
+
// the keys.
|
|
404
|
+
- (BOOL)clearValueAtPath:(NSArray<NSString *> *)path {
|
|
405
|
+
NSString *key = path.firstObject;
|
|
406
|
+
if (key.length == 0) {
|
|
407
|
+
return NO;
|
|
408
|
+
}
|
|
409
|
+
NSArray<ANNOptionField *> *fields = ANNFieldsOf(self.class);
|
|
410
|
+
NSString *property = nil;
|
|
411
|
+
if (fields.count) {
|
|
412
|
+
for (ANNOptionField *field in fields) {
|
|
413
|
+
if ([field.key isEqualToString:key]) {
|
|
414
|
+
property = field.property;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (!property) {
|
|
419
|
+
// A group without a key (the font of a text) holds keys of its parent's object.
|
|
420
|
+
for (ANNOptionField *field in fields) {
|
|
421
|
+
if (field.kind == ANNOptionFieldKindGroup && !field.key &&
|
|
422
|
+
[[self valueForKey:field.property] clearValueAtPath:path]) {
|
|
423
|
+
return YES;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return NO;
|
|
427
|
+
}
|
|
428
|
+
} else if ([ANNValuePropertiesOf(self.class) containsObject:key]) {
|
|
429
|
+
property = key;
|
|
430
|
+
} else {
|
|
431
|
+
return NO;
|
|
432
|
+
}
|
|
433
|
+
if (path.count == 1) {
|
|
434
|
+
ANNOptions *fresh = [[self.class alloc] initWithDict:nil];
|
|
435
|
+
[self setValue:[fresh valueForKey:property] forKey:property];
|
|
436
|
+
return YES;
|
|
437
|
+
}
|
|
438
|
+
id value = [self valueForKey:property];
|
|
439
|
+
if (![value isKindOfClass:ANNOptions.class]) {
|
|
440
|
+
return NO;
|
|
441
|
+
}
|
|
442
|
+
NSArray<NSString *> *rest = [path subarrayWithRange:NSMakeRange(1, path.count - 1)];
|
|
443
|
+
return [(ANNOptions *)value clearValueAtPath:rest];
|
|
444
|
+
}
|
|
445
|
+
|
|
313
446
|
- (id)copyWithZone:(NSZone *)zone {
|
|
314
447
|
ANNOptions *copy = [[self.class alloc] initWithDict:nil];
|
|
315
448
|
[copy mergeOptions:self];
|
|
@@ -326,4 +459,27 @@ static NSArray *ANNCopyItems(NSArray *items) {
|
|
|
326
459
|
return result;
|
|
327
460
|
}
|
|
328
461
|
|
|
462
|
+
- (BOOL)isSameValueAs:(ANNOptions *)other {
|
|
463
|
+
if (other == self) {
|
|
464
|
+
return YES;
|
|
465
|
+
}
|
|
466
|
+
if (other.class != self.class) {
|
|
467
|
+
return NO;
|
|
468
|
+
}
|
|
469
|
+
for (NSString *property in ANNValuePropertiesOf(self.class)) {
|
|
470
|
+
if (!ANNOptionValuesSame([self valueForKey:property], [other valueForKey:property])) {
|
|
471
|
+
return NO;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return YES;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
- (NSUInteger)valueHash {
|
|
478
|
+
NSUInteger hash = (NSUInteger)(__bridge void *)self.class;
|
|
479
|
+
for (NSString *property in ANNValuePropertiesOf(self.class)) {
|
|
480
|
+
hash = hash * 31 + ANNOptionValueHash([self valueForKey:property]);
|
|
481
|
+
}
|
|
482
|
+
return hash;
|
|
483
|
+
}
|
|
484
|
+
|
|
329
485
|
@end
|
|
@@ -1,12 +1,119 @@
|
|
|
1
1
|
#import "ANNOptionsReader.h"
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
3
|
#import "ANNNavigationOptions.h"
|
|
4
|
+
#if DEBUG
|
|
5
|
+
#import <React/RCTLog.h>
|
|
6
|
+
#endif
|
|
4
7
|
|
|
5
8
|
// The parse runs on the main queue, one tree at a time: the path and the first failure are its state. A group read
|
|
6
9
|
// outside a parse (a module that reads its own JSON) finds an empty path and keeps no failure.
|
|
7
10
|
static NSMutableArray<NSString *> *ANNPathComponents;
|
|
8
11
|
static NSString *ANNFirstFailure;
|
|
9
12
|
|
|
13
|
+
#if DEBUG
|
|
14
|
+
// A deep mutable copy of `json` with `value` at `path` (keys, and indexes of arrays); nil `value` removes the key.
|
|
15
|
+
static id ANNJSONWith(id json, NSArray *path, id value) {
|
|
16
|
+
if (path.count == 0) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
NSArray *rest = [path subarrayWithRange:NSMakeRange(1, path.count - 1)];
|
|
20
|
+
if ([json isKindOfClass:NSArray.class]) {
|
|
21
|
+
NSMutableArray *items = [json mutableCopy];
|
|
22
|
+
NSUInteger index = [path[0] unsignedIntegerValue];
|
|
23
|
+
items[index] = ANNJSONWith(items[index], rest, value);
|
|
24
|
+
return items;
|
|
25
|
+
}
|
|
26
|
+
NSMutableDictionary *fields = [json mutableCopy];
|
|
27
|
+
id inner = ANNJSONWith(fields[path[0]], rest, value);
|
|
28
|
+
if (inner) {
|
|
29
|
+
fields[path[0]] = inner;
|
|
30
|
+
} else {
|
|
31
|
+
[fields removeObjectForKey:path[0]];
|
|
32
|
+
}
|
|
33
|
+
return fields;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// There is no iOS test target: in development the value equality of the options (`isSameValueAs:`) checks itself
|
|
37
|
+
// once on a tree with a value of each kind: two reads, a copy and a merge give the same options and the same hash, and
|
|
38
|
+
// a change of one value at any depth gives other options. A module's group (the menu) compares by its properties.
|
|
39
|
+
static void ANNCheckSameValues(void) {
|
|
40
|
+
NSDictionary *button = @{
|
|
41
|
+
@"id" : @"save",
|
|
42
|
+
@"text" : @"Save",
|
|
43
|
+
@"icon" : @{@"system" : @"star"},
|
|
44
|
+
@"enabled" : @YES,
|
|
45
|
+
@"badge" : @1,
|
|
46
|
+
@"fontSize" : @15,
|
|
47
|
+
@"menu" : @{
|
|
48
|
+
@"title" : @"More",
|
|
49
|
+
@"items" : @[ @{@"id" : @"details", @"title" : @"Details", @"checked" : @YES} ],
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
NSDictionary *json = @{
|
|
53
|
+
@"topBar" : @{
|
|
54
|
+
@"title" : @{@"text" : @"Home", @"color" : @(0xFF112233)},
|
|
55
|
+
@"rightButtonColor" : @{@"light" : @(0xFF000000), @"dark" : @(0xFFFFFFFF)},
|
|
56
|
+
@"leftButtonColor" : NSNull.null,
|
|
57
|
+
@"rightButtons" : @[ button ],
|
|
58
|
+
@"leftButtons" : @[],
|
|
59
|
+
@"height" : @44,
|
|
60
|
+
},
|
|
61
|
+
@"keyCommands" : @[ @{@"input" : @"k", @"modifiers" : @[ @"command" ]} ],
|
|
62
|
+
};
|
|
63
|
+
ANNNavigationOptions *options = [[ANNNavigationOptions alloc] initWithDict:json];
|
|
64
|
+
ANNNavigationOptions *again = [[ANNNavigationOptions alloc] initWithDict:json];
|
|
65
|
+
ANNNavigationOptions *copy = [options copy];
|
|
66
|
+
ANNNavigationOptions *overEmpty = [options withDefault:[ANNNavigationOptions new]];
|
|
67
|
+
for (ANNNavigationOptions *same in @[ again, copy, overEmpty ]) {
|
|
68
|
+
if (![options isSameValueAs:same] || options.valueHash != same.valueHash) {
|
|
69
|
+
RCTLogError(@"[ApexNavigation] options read, copied or resolved from the same JSON are "
|
|
70
|
+
@"not the same value");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Each change: the path of one value and its new value; an unset key is not the same as `null` or `[]`.
|
|
74
|
+
NSDictionary<NSString *, NSArray *> *changes = @{
|
|
75
|
+
@"a text" : @[ @[ @"topBar", @"title", @"text" ], @"Profile" ],
|
|
76
|
+
@"a color" : @[ @[ @"topBar", @"title", @"color" ], @(0xFF112234) ],
|
|
77
|
+
@"a side of a color pair" : @[ @[ @"topBar", @"rightButtonColor", @"dark" ], @(0xFFEEEEEE) ],
|
|
78
|
+
@"a number" : @[ @[ @"topBar", @"height" ], @45 ],
|
|
79
|
+
@"an image" : @[ @[ @"topBar", @"rightButtons", @0, @"icon", @"system" ], @"heart" ],
|
|
80
|
+
@"a boolean" : @[ @[ @"topBar", @"rightButtons", @0, @"enabled" ], @NO ],
|
|
81
|
+
@"a boolean for a number" : @[ @[ @"topBar", @"rightButtons", @0, @"badge" ], @YES ],
|
|
82
|
+
@"a font" : @[ @[ @"topBar", @"rightButtons", @0, @"fontSize" ], @16 ],
|
|
83
|
+
@"a module's group" :
|
|
84
|
+
@[ @[ @"topBar", @"rightButtons", @0, @"menu", @"items", @0, @"checked" ], @NO ],
|
|
85
|
+
@"an object of an array" : @[ @[ @"keyCommands", @0, @"modifiers", @0 ], @"shift" ],
|
|
86
|
+
};
|
|
87
|
+
NSDictionary<NSString *, NSArray *> *removals = @{
|
|
88
|
+
@"a color of `null` and no color" : @[ @"topBar", @"leftButtonColor" ],
|
|
89
|
+
@"an empty array and no array" : @[ @"topBar", @"leftButtons" ],
|
|
90
|
+
};
|
|
91
|
+
NSMutableDictionary<NSString *, id> *variants = [NSMutableDictionary new];
|
|
92
|
+
[changes enumerateKeysAndObjectsUsingBlock:^(NSString *what, NSArray *change, BOOL *stop) {
|
|
93
|
+
variants[what] = ANNJSONWith(json, change[0], change[1]);
|
|
94
|
+
}];
|
|
95
|
+
[removals enumerateKeysAndObjectsUsingBlock:^(NSString *what, NSArray *path, BOOL *stop) {
|
|
96
|
+
variants[what] = ANNJSONWith(json, path, nil);
|
|
97
|
+
}];
|
|
98
|
+
[variants enumerateKeysAndObjectsUsingBlock:^(NSString *what, NSDictionary *variant,
|
|
99
|
+
BOOL *stop) {
|
|
100
|
+
ANNNavigationOptions *changed = [[ANNNavigationOptions alloc] initWithDict:variant];
|
|
101
|
+
if ([options isSameValueAs:changed] || [changed isSameValueAs:options]) {
|
|
102
|
+
RCTLogError(@"[ApexNavigation] options that differ in %@ are the same value", what);
|
|
103
|
+
}
|
|
104
|
+
}];
|
|
105
|
+
ANNNavigationOptions *merged = [options copy];
|
|
106
|
+
[merged mergeOptions:[[ANNNavigationOptions alloc]
|
|
107
|
+
initWithDict:@{@"topBar" : @{@"title" : @{@"text" : @"Profile"}}}]];
|
|
108
|
+
ANNNavigationOptions *read = [[ANNNavigationOptions alloc]
|
|
109
|
+
initWithDict:ANNJSONWith(json, @[ @"topBar", @"title", @"text" ], @"Profile")];
|
|
110
|
+
if (![merged isSameValueAs:read] || merged.valueHash != read.valueHash) {
|
|
111
|
+
RCTLogError(@"[ApexNavigation] merged options are not the same value as the same options "
|
|
112
|
+
@"read at once");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
#endif
|
|
116
|
+
|
|
10
117
|
@implementation ANNOptionsReader
|
|
11
118
|
|
|
12
119
|
+ (ANNNavigationOptions *)optionsFrom:(NSDictionary *)json unreadablePath:(NSString **)unreadablePath {
|
|
@@ -15,6 +122,7 @@ static NSString *ANNFirstFailure;
|
|
|
15
122
|
static dispatch_once_t checkOnce;
|
|
16
123
|
dispatch_once(&checkOnce, ^{
|
|
17
124
|
ANNCheckOptionClassesFrom(ANNNavigationOptions.class);
|
|
125
|
+
ANNCheckSameValues();
|
|
18
126
|
});
|
|
19
127
|
#endif
|
|
20
128
|
NSMutableArray<NSString *> *outerPath = ANNPathComponents;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#import "ANNStackAnimationOptions.h"
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
/// `animations.pop` (`StackPopAnimationOptions`).
|
|
6
|
+
@interface ANNStackPopAnimationOptions : ANNStackAnimationOptions
|
|
7
|
+
|
|
8
|
+
/// The swipe back plays the screen's own pop under the finger.
|
|
9
|
+
@property(nonatomic, strong) Bool *interactive;
|
|
10
|
+
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#import "ANNStackPopAnimationOptions.h"
|
|
2
|
+
|
|
3
|
+
@implementation ANNStackPopAnimationOptions
|
|
4
|
+
|
|
5
|
+
+ (NSArray<ANNOptionField *> *)optionFields {
|
|
6
|
+
return [[super optionFields] arrayByAddingObjectsFromArray:@[
|
|
7
|
+
[ANNOptionField boolField:@"interactive"],
|
|
8
|
+
]];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@end
|
|
@@ -8,6 +8,14 @@ BOOL ANNJSONIsBoolean(id _Nullable value);
|
|
|
8
8
|
/// A JSON number that is finite: never a boolean, `NaN` or an infinity.
|
|
9
9
|
BOOL ANNJSONIsNumber(id _Nullable value);
|
|
10
10
|
|
|
11
|
+
/// Two values as the options keep them are the same: a boolean is never the same as a number, arrays and objects
|
|
12
|
+
/// compare item by item, anything else (a string, a number, `null`, a color) by `isEqual:`. nil is the same only as
|
|
13
|
+
/// nil.
|
|
14
|
+
BOOL ANNJSONValuesSame(id _Nullable a, id _Nullable b);
|
|
15
|
+
|
|
16
|
+
/// A hash of a value `ANNJSONValuesSame` compares: values that are the same have the same hash.
|
|
17
|
+
NSUInteger ANNJSONValueHash(id _Nullable value);
|
|
18
|
+
|
|
11
19
|
/// One value of the options as the JSON gave it. Immutable: trees of options may share it. `null` is a value too where
|
|
12
20
|
/// the kind has an explicit "none" (a text, a color): it takes the place of what the layers under it gave.
|
|
13
21
|
@interface ANNParam : NSObject
|
|
@@ -23,6 +31,13 @@ BOOL ANNJSONIsNumber(id _Nullable value);
|
|
|
23
31
|
/// The options gave `null`.
|
|
24
32
|
- (BOOL)isNull;
|
|
25
33
|
|
|
34
|
+
/// A param of the same class with the same value: two reads of the same JSON are the same, two params without a value
|
|
35
|
+
/// are the same. `==` and `isEqual:` stay identity.
|
|
36
|
+
- (BOOL)isSameValueAs:(nullable ANNParam *)other;
|
|
37
|
+
|
|
38
|
+
/// A hash consistent with `isSameValueAs:`.
|
|
39
|
+
- (NSUInteger)valueHash;
|
|
40
|
+
|
|
26
41
|
@end
|
|
27
42
|
|
|
28
43
|
NS_ASSUME_NONNULL_END
|
|
@@ -9,6 +9,56 @@ BOOL ANNJSONIsNumber(id value) {
|
|
|
9
9
|
return [value isKindOfClass:NSNumber.class] && !ANNJSONIsBoolean(value) && isfinite([value doubleValue]);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
BOOL ANNJSONValuesSame(id a, id b) {
|
|
13
|
+
if (a == b) {
|
|
14
|
+
return YES;
|
|
15
|
+
}
|
|
16
|
+
if (!a || !b) {
|
|
17
|
+
return NO;
|
|
18
|
+
}
|
|
19
|
+
if ([a isKindOfClass:NSArray.class]) {
|
|
20
|
+
if (![b isKindOfClass:NSArray.class] || [a count] != [b count]) {
|
|
21
|
+
return NO;
|
|
22
|
+
}
|
|
23
|
+
NSArray *items = b;
|
|
24
|
+
__block BOOL same = YES;
|
|
25
|
+
[(NSArray *)a enumerateObjectsUsingBlock:^(id item, NSUInteger index, BOOL *stop) {
|
|
26
|
+
same = ANNJSONValuesSame(item, items[index]);
|
|
27
|
+
*stop = !same;
|
|
28
|
+
}];
|
|
29
|
+
return same;
|
|
30
|
+
}
|
|
31
|
+
if ([a isKindOfClass:NSDictionary.class]) {
|
|
32
|
+
if (![b isKindOfClass:NSDictionary.class] || [a count] != [b count]) {
|
|
33
|
+
return NO;
|
|
34
|
+
}
|
|
35
|
+
NSDictionary *fields = b;
|
|
36
|
+
__block BOOL same = YES;
|
|
37
|
+
[(NSDictionary *)a enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
|
|
38
|
+
same = ANNJSONValuesSame(value, fields[key]);
|
|
39
|
+
*stop = !same;
|
|
40
|
+
}];
|
|
41
|
+
return same;
|
|
42
|
+
}
|
|
43
|
+
// `isEqual:` of NSNumber takes `true` for 1: a badge of `true` (a dot) and of `1` (a number) are not the same.
|
|
44
|
+
if (ANNJSONIsBoolean(a) != ANNJSONIsBoolean(b)) {
|
|
45
|
+
return NO;
|
|
46
|
+
}
|
|
47
|
+
return [a isEqual:b];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// An object's hash is its count only (Foundation's); an array mixes its items in, so lists of other values differ.
|
|
51
|
+
NSUInteger ANNJSONValueHash(id value) {
|
|
52
|
+
if ([value isKindOfClass:NSArray.class]) {
|
|
53
|
+
NSUInteger hash = [value count];
|
|
54
|
+
for (id item in value) {
|
|
55
|
+
hash = hash * 31 + ANNJSONValueHash(item);
|
|
56
|
+
}
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
return [value hash];
|
|
60
|
+
}
|
|
61
|
+
|
|
12
62
|
@implementation ANNParam
|
|
13
63
|
|
|
14
64
|
- (instancetype)initWithValue:(id)value {
|
|
@@ -26,4 +76,15 @@ BOOL ANNJSONIsNumber(id value) {
|
|
|
26
76
|
return _value == NSNull.null;
|
|
27
77
|
}
|
|
28
78
|
|
|
79
|
+
- (BOOL)isSameValueAs:(ANNParam *)other {
|
|
80
|
+
if (other == self) {
|
|
81
|
+
return YES;
|
|
82
|
+
}
|
|
83
|
+
return other.class == self.class && ANNJSONValuesSame(_value, other.value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
- (NSUInteger)valueHash {
|
|
87
|
+
return ANNJSONValueHash(_value);
|
|
88
|
+
}
|
|
89
|
+
|
|
29
90
|
@end
|
|
@@ -38,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
38
38
|
/// A pair that has only one of its sides.
|
|
39
39
|
- (BOOL)isPartial;
|
|
40
40
|
|
|
41
|
+
/// The same color: a pair by its sides (its dynamic color is made anew at each read), anything else by its value.
|
|
42
|
+
- (BOOL)isSameValueAs:(nullable ANNParam *)other;
|
|
43
|
+
|
|
41
44
|
@end
|
|
42
45
|
|
|
43
46
|
NS_ASSUME_NONNULL_END
|