@apexnative/navigation 1.0.0-alpha.6 → 1.0.0-alpha.8

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.
Files changed (242) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +15 -0
  3. package/android/CLAUDE.md +19 -10
  4. package/android/CORE.md +3 -2
  5. package/android/consumer-rules.pro +6 -0
  6. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  7. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
  8. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  9. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  10. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  11. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  12. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  13. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  14. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  15. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  16. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  17. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  18. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +4 -0
  19. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
  20. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
  21. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
  22. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
  23. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  24. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  25. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +329 -55
  26. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +26 -5
  27. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
  28. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +46 -7
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +7 -4
  33. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +86 -39
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
  40. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +124 -181
  41. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  42. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  43. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +56 -3
  44. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +170 -153
  45. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  46. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -45
  47. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/{TextChangeAnimator.kt → TextMorph.kt} +79 -129
  48. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  49. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  50. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  51. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  52. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  53. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  54. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  55. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  56. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  57. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  58. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  59. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  60. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  61. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  62. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  63. package/android/src/main/res/values/styles.xml +1 -1
  64. package/android/src/main/res/values-night/styles.xml +1 -1
  65. package/cli/doctor/checks/app.js +16 -1
  66. package/cli/project.js +4 -0
  67. package/cli/setup/index.js +5 -0
  68. package/cli/setup/steps/metro.js +46 -0
  69. package/docs-md/architecture/native-screens.md +4 -0
  70. package/docs-md/architecture/theme-and-context.md +131 -12
  71. package/docs-md/architecture/transitions.md +43 -4
  72. package/docs-md/flows/bottom-sheets.md +4 -2
  73. package/docs-md/fundamentals/screens-and-props.md +9 -1
  74. package/docs-md/guides/cli.md +3 -1
  75. package/docs-md/help/troubleshooting.md +1 -1
  76. package/docs-md/index.json +29 -1
  77. package/docs-md/screen-ui/back-and-gestures.md +3 -1
  78. package/docs-md/screen-ui/bar-buttons.md +1 -1
  79. package/docs-md/screen-ui/toolbar-and-fab.md +2 -0
  80. package/docs-md/start/installation.md +16 -1
  81. package/ios/Animations/ANNFrameDriver.h +55 -15
  82. package/ios/Animations/ANNFrameDriver.mm +214 -60
  83. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  84. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  85. package/ios/Animations/ANNTransitionPlan.h +2 -2
  86. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  87. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  88. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  89. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  90. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  91. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  92. package/ios/Animations/Elements/ANNSharedElementFlight.mm +181 -43
  93. package/ios/Animations/Interpolators/ANNViewTracks.h +6 -5
  94. package/ios/Animations/Interpolators/ANNViewTracks.mm +51 -21
  95. package/ios/CLAUDE.md +19 -8
  96. package/ios/CORE.md +1 -1
  97. package/ios/Core/ANNAppDelegate.mm +0 -2
  98. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  99. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  100. package/ios/Core/ANNSceneDelegate.mm +2 -2
  101. package/ios/Core/ANNUtils.h +7 -0
  102. package/ios/Core/ANNUtils.mm +10 -0
  103. package/ios/Core/ApexNavigation.mm +6 -2
  104. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +1 -7
  105. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  106. package/ios/Layouts/Screen/ANNComponentViewController.mm +4 -1
  107. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  108. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  109. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  110. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  111. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  112. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  113. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  114. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +2 -4
  115. package/ios/Options/ANNAnimationOptions.h +2 -1
  116. package/ios/Options/ANNAnimationOptions.mm +1 -1
  117. package/ios/Options/ANNOptions.h +7 -0
  118. package/ios/Options/ANNOptions.mm +45 -0
  119. package/ios/Options/ANNOptionsReader.mm +2 -4
  120. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  121. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  122. package/ios/Options/Params/ColorParser.h +4 -3
  123. package/ios/Options/Params/ColorParser.mm +22 -4
  124. package/ios/React/ANNSurfaceHostingView.h +8 -0
  125. package/ios/React/ANNSurfaceHostingView.mm +28 -1
  126. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  127. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  128. package/ios/TurboModules/ANNTurboModule.mm +46 -0
  129. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  130. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  131. package/lib/module/ARCHITECTURE.md +9 -3
  132. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  133. package/lib/module/api/app/AppRegistry.js +14 -2
  134. package/lib/module/api/app/AppRegistry.js.map +1 -1
  135. package/lib/module/api/app/createNavigation.js +188 -66
  136. package/lib/module/api/app/createNavigation.js.map +1 -1
  137. package/lib/module/api/events/navigationEvents.js +6 -2
  138. package/lib/module/api/events/navigationEvents.js.map +1 -1
  139. package/lib/module/api/linking/linking.js +11 -0
  140. package/lib/module/api/linking/linking.js.map +1 -1
  141. package/lib/module/api/screens/defineScreen.js +45 -3
  142. package/lib/module/api/screens/defineScreen.js.map +1 -1
  143. package/lib/module/api/screens/nativeScreens.js +31 -6
  144. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  145. package/lib/module/api/screens/registerScreens.js +193 -35
  146. package/lib/module/api/screens/registerScreens.js.map +1 -1
  147. package/lib/module/api/theme/theme.js +5 -0
  148. package/lib/module/api/theme/theme.js.map +1 -1
  149. package/lib/module/engine/commands.js +21 -1
  150. package/lib/module/engine/commands.js.map +1 -1
  151. package/lib/module/engine/layout.js +7 -2
  152. package/lib/module/engine/layout.js.map +1 -1
  153. package/lib/module/engine/nativeScreens.js +9 -0
  154. package/lib/module/engine/nativeScreens.js.map +1 -1
  155. package/lib/module/engine/options.js +5 -4
  156. package/lib/module/engine/options.js.map +1 -1
  157. package/lib/module/engine/optionsChange.js +102 -0
  158. package/lib/module/engine/optionsChange.js.map +1 -0
  159. package/lib/module/engine/refresh.js +85 -0
  160. package/lib/module/engine/refresh.js.map +1 -0
  161. package/lib/module/engine/rememberedOptions.js +60 -0
  162. package/lib/module/engine/rememberedOptions.js.map +1 -1
  163. package/lib/module/engine/screens.js +26 -0
  164. package/lib/module/engine/screens.js.map +1 -1
  165. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  166. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  167. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  168. package/lib/typescript/api/app/createNavigation.d.ts +5 -0
  169. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  170. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  171. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  172. package/lib/typescript/api/index.d.ts +1 -1
  173. package/lib/typescript/api/index.d.ts.map +1 -1
  174. package/lib/typescript/api/linking/linking.d.ts +8 -0
  175. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  176. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  177. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  178. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  179. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  180. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  181. package/lib/typescript/api/theme/theme.d.ts +2 -0
  182. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  183. package/lib/typescript/engine/commands.d.ts +5 -0
  184. package/lib/typescript/engine/commands.d.ts.map +1 -1
  185. package/lib/typescript/engine/layout.d.ts.map +1 -1
  186. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  187. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  188. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  189. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  190. package/lib/typescript/engine/refresh.d.ts +35 -0
  191. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  192. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  193. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  194. package/lib/typescript/engine/screens.d.ts +7 -0
  195. package/lib/typescript/engine/screens.d.ts.map +1 -1
  196. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  197. package/lib/typescript/interfaces/options/animations.d.ts +40 -15
  198. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  199. package/lib/typescript/interfaces/options/bottomTabs.d.ts +2 -2
  200. package/lib/typescript/interfaces/options/buttons.d.ts +2 -2
  201. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  202. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  203. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  204. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  205. package/lib/typescript/interfaces/options/topBar.d.ts +14 -0
  206. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  207. package/metro/acceptPlugin.js +57 -0
  208. package/metro/babelTransformer.js +45 -0
  209. package/metro/index.js +36 -0
  210. package/package.json +3 -1
  211. package/src/ARCHITECTURE.md +9 -3
  212. package/src/adapters/NativeANNTurboModule.ts +7 -0
  213. package/src/api/app/AppRegistry.ts +16 -2
  214. package/src/api/app/createNavigation.ts +198 -66
  215. package/src/api/events/navigationEvents.ts +9 -3
  216. package/src/api/index.ts +1 -0
  217. package/src/api/linking/linking.ts +22 -1
  218. package/src/api/screens/defineScreen.ts +49 -5
  219. package/src/api/screens/nativeScreens.ts +40 -4
  220. package/src/api/screens/registerScreens.tsx +185 -42
  221. package/src/api/theme/theme.ts +5 -0
  222. package/src/engine/commands.ts +21 -0
  223. package/src/engine/layout.ts +11 -2
  224. package/src/engine/nativeScreens.ts +10 -1
  225. package/src/engine/options.ts +6 -8
  226. package/src/engine/optionsChange.ts +107 -0
  227. package/src/engine/refresh.ts +97 -0
  228. package/src/engine/rememberedOptions.ts +70 -0
  229. package/src/engine/screens.tsx +24 -0
  230. package/src/interfaces/ComponentEvents.ts +2 -2
  231. package/src/interfaces/options/animations.ts +40 -15
  232. package/src/interfaces/options/bottomTabs.ts +2 -2
  233. package/src/interfaces/options/buttons.ts +2 -2
  234. package/src/interfaces/options/modals.ts +6 -7
  235. package/src/interfaces/options/stack.ts +8 -3
  236. package/src/interfaces/options/topBar.ts +14 -0
  237. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  238. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -134
  239. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -78
  240. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  241. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -57
  242. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -146
@@ -1,8 +1,11 @@
1
1
  package dev.apexnative.navigation.react
2
2
 
3
3
  import android.content.Context
4
+ import com.facebook.react.bridge.ReadableArray
4
5
  import com.facebook.react.bridge.ReadableMap
6
+ import com.facebook.react.bridge.ReadableType
5
7
  import dev.apexnative.navigation.options.DefaultOptions
8
+ import dev.apexnative.navigation.options.OptionPaths
6
9
  import dev.apexnative.navigation.options.Options
7
10
  import dev.apexnative.navigation.options.OptionsReader
8
11
  import dev.apexnative.navigation.system.AppDirection
@@ -10,9 +13,9 @@ import dev.apexnative.navigation.viewcontrollers.navigator.Navigators
10
13
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
11
14
 
12
15
  /**
13
- * The commands of the options, `setDefaultOptions` and `mergeOptions`: both without a promise. Their options are read
14
- * on the module thread, in the runner's step before the main task; the main task stores and applies them. What can't be
15
- * read is logged (`[ApexNavigation] <method>: <text>`); the app goes on.
16
+ * The commands of the options, `setDefaultOptions`, `mergeOptions` and `clearOptions`: all without a promise. Their
17
+ * options and paths are read on the module thread, in the runner's step before the main task; the main task stores
18
+ * and applies them. What can't be read is logged (`[ApexNavigation] <method>: <text>`); the app goes on.
16
19
  */
17
20
  internal class OptionsCommands(
18
21
  private val runner: CommandRunner,
@@ -30,6 +33,11 @@ internal class OptionsCommands(
30
33
  * system UI follows (`Navigator.optionsMerged`, `mergeOptions` step 5). Main thread.
31
34
  */
32
35
  private val merged: (ViewController<*>, Options) -> Unit = { _, _ -> },
36
+ /**
37
+ * Repaints what shows in the current navigation activity, as [repaint] does without new default options
38
+ * (`Navigator.repaint`), after `clearOptions`. Main thread.
39
+ */
40
+ private val repaintCurrent: () -> Unit = {},
33
41
  ) {
34
42
  constructor(runner: CommandRunner, context: () -> Context?) : this(
35
43
  runner = runner,
@@ -40,6 +48,7 @@ internal class OptionsCommands(
40
48
  Navigators.all().forEach { it.navigator?.defaultOptionsChanged(direction) }
41
49
  },
42
50
  merged = { node, options -> Navigators.current()?.navigator?.optionsMerged(node, options) },
51
+ repaintCurrent = { Navigators.current()?.navigator?.repaint() },
43
52
  )
44
53
 
45
54
  /**
@@ -82,8 +91,37 @@ internal class OptionsCommands(
82
91
  merged(node, read.options)
83
92
  }
84
93
 
94
+ /**
95
+ * `clearOptions` (development: Fast Refresh, after an edit took options out of a screen that shows): the values at
96
+ * [paths] (`topBar.subtitle`; a group's path for the whole group) leave the own options of the node [componentId]
97
+ * names, through `ViewController.changeOptions` ([OptionPaths.clear]), so it resolves to what the layouts around it
98
+ * and the defaults give there; the user layer stays. The stack it is in writes its back arrow's visibility into
99
+ * them again (`StackController.ownOptionsCleared`), then what shows is repainted as after `setDefaultOptions`. No
100
+ * node with that id, or no path that names an option: nothing, not even a log line. Without an activity: one log
101
+ * line, as `mergeOptions`.
102
+ */
103
+ fun clearOptions(componentId: String, paths: ReadableArray) = runner.runVoid(
104
+ name = CLEAR_OPTIONS,
105
+ what = "clear the options of the screen '$componentId'",
106
+ prepare = {
107
+ (0 until paths.size()).filter { paths.getType(it) == ReadableType.String }.mapNotNull(paths::getString)
108
+ },
109
+ ) { read ->
110
+ if (!runner.hasActivity()) {
111
+ runner.logFailure(CLEAR_OPTIONS, ErrorTexts.nowhereToShow())
112
+ return@runVoid
113
+ }
114
+ val node = findNode(componentId) ?: return@runVoid
115
+ val known = read.filter { OptionPaths.find(node.options, it) != null }
116
+ if (known.isEmpty()) return@runVoid
117
+ node.changeOptions { own -> known.forEach { OptionPaths.clear(own, it) } }
118
+ node.findParentStack()?.ownOptionsCleared(node)
119
+ repaintCurrent()
120
+ }
121
+
85
122
  private companion object {
86
123
  const val SET_DEFAULT_OPTIONS = "setDefaultOptions"
87
124
  const val MERGE_OPTIONS = "mergeOptions"
125
+ const val CLEAR_OPTIONS = "clearOptions"
88
126
  }
89
127
  }
@@ -3,6 +3,7 @@ package dev.apexnative.navigation.react
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.os.Bundle
6
+ import android.util.Size
6
7
  import android.view.MotionEvent
7
8
  import android.view.ViewGroup
8
9
  import android.widget.FrameLayout
@@ -28,7 +29,9 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListe
28
29
  * never makes this one ready. A screen without a safe area view (no view ever told anything of its id) is ready once
29
30
  * the root view of its surface has children, read on this view's own layout passes. A root that renders at once
30
31
  * ([rendersAtOnce]: a component of a bar, a tab's cell) marks its id so from before its surface starts until it stops.
31
- * Ready once, ready for good; what waits for it ([whenReady]) runs then, once.
32
+ * Ready once, ready for good; what waits for it ([whenReady]) runs then, once. Its content measures without a height
33
+ * limit ([contentSize], `ContentHeights`), and the handlers of [setOnContentMounted] hear each mount of Fabric while
34
+ * the view is in a window.
32
35
  * Without a signal or children, the limits of what waits decide: the view has no timer of its own. It owns the
33
36
  * record of its appearance, which the dispatcher of the appearance events holds until the content is ready
34
37
  * (`AppearanceDispatcher`).
@@ -51,6 +54,7 @@ class ReactView @JvmOverloads constructor(
51
54
  private var readyTold = false
52
55
  private val readyListeners = mutableListOf<Runnable>()
53
56
  private var listening = false
57
+ private val mounts = ContentMounts { !destroyed && isAttachedToWindow }
54
58
 
55
59
  // What this root hears of its id: only the signals of its own surface.
56
60
  private val readiness = object : ScreenReadiness.Listener {
@@ -112,6 +116,16 @@ class ReactView @JvmOverloads constructor(
112
116
  override val touchRoot: ViewGroup?
113
117
  get() = surface?.view as? ViewGroup
114
118
 
119
+ override fun contentSize(maxWidth: Int): Size? {
120
+ val surface = surface ?: return null
121
+ val height = ContentHeights.surfaceHeight(context, surface, maxWidth) ?: return null
122
+ return Size(maxWidth, height)
123
+ }
124
+
125
+ override fun setOnContentMounted(key: String, handler: Runnable?) {
126
+ if (!destroyed) mounts.set(key, handler)
127
+ }
128
+
115
129
  override fun isReady(): Boolean {
116
130
  if (!ready && hasRendered()) ready = true
117
131
  return ready
@@ -167,6 +181,7 @@ class ReactView @JvmOverloads constructor(
167
181
  AppearanceDispatcher.didDisappear(appearance)
168
182
  destroyed = true
169
183
  readyListeners.clear()
184
+ mounts.clear()
170
185
  stopListening()
171
186
  if (rendersAtOnce) ScreenReadiness.setRendersAtOnce(componentId, this, false)
172
187
  val surface = surface ?: return
@@ -2,9 +2,13 @@ package dev.apexnative.navigation.slots
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.util.Size
5
6
  import android.view.MotionEvent
6
7
  import android.view.ViewGroup
7
8
  import android.widget.FrameLayout
9
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
10
+ import dev.apexnative.navigation.react.ContentHeights
11
+ import dev.apexnative.navigation.react.ContentMounts
8
12
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
9
13
  import dev.apexnative.navigation.react.events.ComponentType
10
14
  import dev.apexnative.navigation.react.events.ScreenAppearance
@@ -16,9 +20,10 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListe
16
20
  * What a slot screen (`slot:<Name>`) shows: the empty container its slot of the app's React tree moves its children
17
21
  * into, under the screen's id ([SlotContainers]), told of each change of its size. It is the screen's content for the
18
22
  * core: ready once the slot's content has mounted into it and, when the screen has a safe area view, the screen
19
- * rendered into that view; its mounted handlers run at each mount. Its appearance events go through its record to the
20
- * core's dispatcher (`AppearanceDispatcher`), which holds them until it is ready or the hold limit. Destroyed while the
21
- * id is still its own, it tells JavaScript to take the slot's content away (`ANN.SlotDestroyed`).
23
+ * rendered into that view; its mounted handlers run at each mount of Fabric and as the content mounts into it. Its
24
+ * content measures without a height limit in the slot's own layout ([contentSize]). Its appearance events go through
25
+ * its record to the core's dispatcher (`AppearanceDispatcher`), which holds them until it is ready or the hold limit.
26
+ * Destroyed while the id is still its own, it tells JavaScript to take the slot's content away (`ANN.SlotDestroyed`).
22
27
  */
23
28
  @SuppressLint("ViewConstructor")
24
29
  class SlotScreenView(
@@ -30,7 +35,7 @@ class SlotScreenView(
30
35
  private val scrollEventListener = ScrollEventListener()
31
36
  private val whenMounted = mutableListOf<Runnable>()
32
37
  private val whenRendered = mutableListOf<Runnable>()
33
- private val mountedHandlers = LinkedHashMap<String, Runnable>()
38
+ private val mounts = ContentMounts { !destroyed && isAttachedToWindow }
34
39
  private var listening = false
35
40
 
36
41
  // The safe area view of a slot renders in the app's one React tree: every signal of the id is the screen's.
@@ -84,10 +89,11 @@ class SlotScreenView(
84
89
  destroyed = true
85
90
  whenMounted.clear()
86
91
  whenRendered.clear()
87
- mountedHandlers.clear()
92
+ mounts.clear()
88
93
  stopListening()
89
94
  // A later screen with the same id keeps the slot.
90
95
  if (SlotContainers.get(componentId) !== this) return
96
+ ContentHeights.forgetSlot(componentId)
91
97
  SlotContainers.remove(componentId)
92
98
  if (!isPreparedPush) AppearanceDispatcher.slotDestroyed(componentId, appearance.generation)
93
99
  }
@@ -120,7 +126,21 @@ class SlotScreenView(
120
126
  }
121
127
 
122
128
  override fun setOnContentMounted(key: String, handler: Runnable?) {
123
- if (handler == null) mountedHandlers.remove(key) else mountedHandlers[key] = handler
129
+ if (!destroyed) mounts.set(key, handler)
130
+ }
131
+
132
+ // The slot measures its content in its own layout: the first question asks for it and lays the slot out again; the
133
+ // height comes with that layout, before the mount the handlers hear. The width is the container's, at which the
134
+ // slot lays its content out.
135
+ override fun contentSize(maxWidth: Int): Size? {
136
+ if (destroyed) return null
137
+ if (!ContentHeights.isSlotRequested(componentId)) {
138
+ ContentHeights.requestSlot(componentId)
139
+ SlotContainers.changed(componentId)
140
+ return null
141
+ }
142
+ val height = ContentHeights.slotHeight(componentId)?.takeIf { it > 0f } ?: return null
143
+ return Size(if (width > 0) width else maxWidth, Math.round(height.dpToPx()))
124
144
  }
125
145
 
126
146
  // Only the container that holds the slot of the id: a destroyed one, or one a later screen replaced, takes no
@@ -136,7 +156,7 @@ class SlotScreenView(
136
156
  val waiting = whenMounted.toList()
137
157
  whenMounted.clear()
138
158
  waiting.forEach { it.run() }
139
- mountedHandlers.values.toList().forEach { it.run() }
159
+ mounts.run()
140
160
  checkReadiness()
141
161
  }
142
162
 
@@ -15,6 +15,7 @@ import com.facebook.react.uimanager.ThemedReactContext
15
15
  import com.facebook.react.uimanager.UIManagerHelper
16
16
  import com.facebook.react.uimanager.events.EventDispatcher
17
17
  import com.facebook.react.views.view.ReactViewGroup
18
+ import dev.apexnative.navigation.react.ContentHeights
18
19
 
19
20
  /**
20
21
  * The React children of a slot. They live outside the views of the host's surface, in the container of a screen, so
@@ -79,6 +80,8 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
79
80
  }
80
81
  private var lastWidth = -1f
81
82
  private var lastHeight = -1f
83
+ // The slot was laid out once more, at the same size, for the measure of its content (`ContentHeights`).
84
+ private var measureAsked = false
82
85
 
83
86
  private val slotId: String?
84
87
  get() = getTag(R.id.view_tag_native_id) as? String
@@ -116,7 +119,14 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
116
119
  if (container != null && state != null && container.width > 0 && container.height > 0) {
117
120
  val width = container.width.toFloat().pxToDp()
118
121
  val height = container.height.toFloat().pxToDp()
119
- if (width != lastWidth || height != lastHeight) {
122
+ // A container that measures the content gets one layout more for it, with the same size, until the height
123
+ // came.
124
+ val requested = ContentHeights.isSlotRequested(id)
125
+ // A request that went (its screen did) and comes again gets its layout again.
126
+ if (!requested) measureAsked = false
127
+ val measures = !measureAsked && requested && ContentHeights.slotHeight(id) == null
128
+ if (width != lastWidth || height != lastHeight || measures) {
129
+ measureAsked = measureAsked || measures
120
130
  lastWidth = width
121
131
  lastHeight = height
122
132
  state.updateState(
@@ -136,6 +146,7 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
136
146
  }
137
147
 
138
148
  fun drop() {
149
+ measureAsked = false
139
150
  SlotContainers.unregister(this)
140
151
  (content.parent as? ViewGroup)?.removeView(content)
141
152
  }
@@ -28,7 +28,7 @@ internal object MaterialTheme {
28
28
  private val wrappers = WeakHashMap<Context, WeakReference<Context>>()
29
29
  // Themes of the navigation's own views, each made once over the activity's with a style of its own.
30
30
  private val followers = WeakHashMap<Context, Int>()
31
- // The popup theme of the bar's toolbars per activity: the app's theme doesn't change while the activity lives.
31
+ // The popup theme of the bar's "⋮" per activity: the app's theme doesn't change while the activity lives.
32
32
  private val popupThemes = WeakHashMap<Activity, Int>()
33
33
 
34
34
  fun context(context: Context): Context {
@@ -38,11 +38,10 @@ internal object MaterialTheme {
38
38
  }
39
39
 
40
40
  /**
41
- * The popup theme of the top bar's toolbars, for "⋮", set on them before their menu is made. None when the app's
42
- * theme follows light and dark: the menu takes the bar's own theme, which [rebase] keeps in the current mode, with
43
- * the app's colors. With a Material 3 theme fixed to light or to dark, a day/night overlay that gives the menu the
44
- * current mode's colors where the theme shows the other mode. Android makes the menu's theme from it at the menu's
45
- * first opening, in the mode of that moment, and no public API makes it again for a later switch.
41
+ * The popup theme of the top bar's "⋮", over the bar's theme at each opening of its popup, in the mode of that
42
+ * moment. None when the app's theme follows light and dark: the menu takes the bar's own theme, which [rebase]
43
+ * keeps in the current mode, with the app's colors. With a Material 3 theme fixed to light or to dark, a day/night
44
+ * overlay that gives the menu the current mode's colors where the theme shows the other mode.
46
45
  */
47
46
  fun popupTheme(context: Context): Int {
48
47
  val activity = activityOf(context) ?: return 0
@@ -0,0 +1,36 @@
1
+ package dev.apexnative.navigation.utils
2
+
3
+ import android.animation.ValueAnimator
4
+ import android.annotation.SuppressLint
5
+ import android.content.Context
6
+ import android.os.Build
7
+ import android.provider.Settings
8
+ import dev.apexnative.navigation.NavigationApplication
9
+
10
+ /**
11
+ * Whether the system plays no animations: "Remove animations" in the accessibility settings, or the animator duration
12
+ * scale at 0 in the developer options. Then a transition doesn't wait for its elements' targets and images and moves no
13
+ * element, and the side menu opens and closes at once. Any thread.
14
+ */
15
+ internal object SystemAnimations {
16
+ /**
17
+ * The animations are off now; [context] reads the scale below Android 8, else the application's. Lint can't see
18
+ * that the decision calls `areAnimatorsEnabled` (API 26) only from Android 8.
19
+ */
20
+ @SuppressLint("NewApi")
21
+ fun areOff(context: Context? = null): Boolean =
22
+ areOff(Build.VERSION.SDK_INT, { ValueAnimator.areAnimatorsEnabled() }) { durationScaleOf(context) }
23
+
24
+ /**
25
+ * The decision: from Android 8 the animator's own answer ([animatorsEnabled]), which follows both settings;
26
+ * below it the duration scale ([durationScale]) at 0.
27
+ */
28
+ fun areOff(sdk: Int, animatorsEnabled: () -> Boolean, durationScale: () -> Float): Boolean =
29
+ if (sdk >= Build.VERSION_CODES.O) !animatorsEnabled() else durationScale() == 0f
30
+
31
+ // Without a context and before the application started (the unit tests), the animations play.
32
+ private fun durationScaleOf(context: Context?): Float {
33
+ val resolver = (context ?: NavigationApplication.instanceOrNull)?.contentResolver ?: return 1f
34
+ return Settings.Global.getFloat(resolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
35
+ }
36
+ }