@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.3
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/CHANGELOG.md +38 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +31 -6
- package/android/CORE.md +7 -2
- package/android/RECIPES.md +4 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +3 -35
- 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 +101 -87
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +76 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- 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 +86 -16
- 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 +41 -23
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +24 -20
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +42 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +29 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +2 -0
- 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 +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +4 -7
- 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 +8 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +16 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +148 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +24 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +51 -11
- 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 +105 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +123 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +19 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +5 -5
- 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 +85 -27
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +5 -4
- 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 +45 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +2 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +4 -3
- package/cli/app.js +1 -1
- package/cli/project.js +6 -2
- package/cli/setup/index.js +13 -0
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/docs-md/architecture/transitions.md +7 -1
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +4 -2
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/guides/deep-links.md +15 -33
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/index.json +8 -0
- package/docs-md/screen-ui/back-and-gestures.md +6 -0
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +1 -0
- package/ios/Animations/ANNTransitionAnimator.mm +13 -11
- 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 +7 -5
- package/ios/Animations/Elements/ANNElementTransition.mm +17 -19
- package/ios/Animations/Elements/ANNSharedElementFlight.h +13 -8
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +59 -24
- package/ios/CLAUDE.md +10 -2
- package/ios/CORE.md +3 -2
- package/ios/Contract/ANNCommandAnswer.h +4 -3
- package/ios/Contract/ANNCommandAnswer.mm +12 -2
- package/ios/Contract/ANNCommandRunner.h +2 -2
- package/ios/Contract/ANNCommandRunner.mm +1 -1
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Core/ANNDeepLinkQueue.h +20 -1
- package/ios/Core/ANNDeepLinkQueue.mm +107 -12
- package/ios/Core/ANNErrorHandler.h +8 -3
- package/ios/Core/ANNErrorHandler.mm +17 -1
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +5 -1
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.mm +6 -4
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +6 -4
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +1 -1
- 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/Root/ANNRootCoordinator.mm +12 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.mm +28 -7
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNScreenInsets.mm +6 -4
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -6
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -7
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/RECIPES.md +2 -1
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +5 -2
- package/lib/module/ARCHITECTURE.md +7 -4
- package/lib/module/api/app/createNavigation.js +2 -0
- package/lib/module/api/app/createNavigation.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/linking/dispatchedLinks.js +23 -0
- package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.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/interfaces/Events.d.ts +9 -0
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +27 -18
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +33 -24
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +7 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +10 -6
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +7 -4
- package/src/api/app/createNavigation.ts +2 -0
- package/src/api/hooks/useNavigation.ts +8 -3
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/interfaces/Events.ts +10 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +27 -18
- package/src/interfaces/options/modals.ts +33 -24
- package/src/interfaces/options/sideMenu.ts +7 -6
- package/src/interfaces/options/system.ts +10 -6
|
@@ -12,147 +12,159 @@ import org.json.JSONArray
|
|
|
12
12
|
/**
|
|
13
13
|
* Sends the events `ANN.<Name>` to the JavaScript of [reactContext] through React Native's device event emitter, in
|
|
14
14
|
* the order they are sent: sent from the main thread only. Without a context, or when its React instance is gone, an
|
|
15
|
-
* event is dropped with one line in the log. The names and the
|
|
16
|
-
* [current]; modules keep making their own.
|
|
15
|
+
* event is dropped with one line in the log. The names and the keys come from [EventContract], made from the types of
|
|
16
|
+
* `src/interfaces/`. The core sends through [current]; modules keep making their own.
|
|
17
17
|
*/
|
|
18
18
|
class EventEmitter(private val reactContext: ReactContext?) {
|
|
19
19
|
/** Whether an event sent now reaches JavaScript. */
|
|
20
20
|
val canSend: Boolean
|
|
21
21
|
get() = reactContext?.hasActiveReactInstance() == true
|
|
22
22
|
|
|
23
|
-
fun emitAppLaunched() = emit(
|
|
23
|
+
fun emitAppLaunched() = emit(EventContract.AppLaunched.EVENT, Arguments.createMap())
|
|
24
24
|
|
|
25
25
|
fun emitCommandCompleted(commandName: String, commandId: String, completionTime: Long) =
|
|
26
|
-
emit(
|
|
27
|
-
putString(
|
|
28
|
-
putString(
|
|
29
|
-
putDouble(
|
|
26
|
+
emit(EventContract.CommandCompleted.EVENT, Arguments.createMap().apply {
|
|
27
|
+
putString(EventContract.CommandCompleted.COMMAND_NAME, commandName)
|
|
28
|
+
putString(EventContract.CommandCompleted.COMMAND_ID, commandId)
|
|
29
|
+
putDouble(EventContract.CommandCompleted.COMPLETION_TIME, completionTime.toDouble())
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
fun emitComponentWillAppear(componentId: String, componentName: String, type: ComponentType) =
|
|
33
|
-
emit(
|
|
33
|
+
emit(EventContract.ComponentWillAppear.EVENT, component(componentId, componentName, type))
|
|
34
34
|
|
|
35
35
|
fun emitComponentDidAppear(componentId: String, componentName: String, type: ComponentType) =
|
|
36
|
-
emit(
|
|
36
|
+
emit(EventContract.ComponentDidAppear.EVENT, component(componentId, componentName, type))
|
|
37
37
|
|
|
38
38
|
fun emitComponentDidDisappear(componentId: String, componentName: String, type: ComponentType) =
|
|
39
|
-
emit(
|
|
39
|
+
emit(EventContract.ComponentDidDisappear.EVENT, component(componentId, componentName, type))
|
|
40
40
|
|
|
41
41
|
/** The screen the user sees (`null` for none) and the selected tab of the tabs it is in (`null` out of tabs). */
|
|
42
42
|
fun emitNavigationStateChanged(screenId: String?, screenName: String?, tab: Int?) =
|
|
43
|
-
emit(
|
|
43
|
+
emit(EventContract.NavigationStateChanged.EVENT, Arguments.createMap().apply {
|
|
44
|
+
val keys = EventContract.NavigationStateChanged
|
|
44
45
|
if (screenId == null) {
|
|
45
|
-
putNull(
|
|
46
|
+
putNull(keys.SCREEN)
|
|
46
47
|
} else {
|
|
47
|
-
putMap(
|
|
48
|
-
putString(
|
|
49
|
-
putString(
|
|
48
|
+
putMap(keys.SCREEN, Arguments.createMap().apply {
|
|
49
|
+
putString(EventContract.NavigationStateChangedScreen.ID, screenId)
|
|
50
|
+
putString(EventContract.NavigationStateChangedScreen.NAME, screenName)
|
|
50
51
|
})
|
|
51
52
|
}
|
|
52
|
-
if (tab == null) putNull(
|
|
53
|
+
if (tab == null) putNull(keys.TAB) else putInt(keys.TAB, tab)
|
|
53
54
|
})
|
|
54
55
|
|
|
55
|
-
fun emitScreenPopped(componentId: String) = emit(
|
|
56
|
+
fun emitScreenPopped(componentId: String) = emit(EventContract.ScreenPopped.EVENT, withId(componentId))
|
|
56
57
|
|
|
57
58
|
fun emitNavigationButtonPressed(componentId: String, buttonId: String) =
|
|
58
|
-
emit(
|
|
59
|
+
emit(EventContract.NavigationButtonPressed.EVENT, withId(componentId).apply {
|
|
60
|
+
putString(EventContract.NavigationButtonPressed.BUTTON_ID, buttonId)
|
|
61
|
+
})
|
|
59
62
|
|
|
60
63
|
fun emitBottomTabSelected(selectedTabIndex: Int, unselectedTabIndex: Int, bottomTabsId: String) =
|
|
61
|
-
emit(
|
|
62
|
-
putInt(
|
|
63
|
-
putInt(
|
|
64
|
-
putString(
|
|
64
|
+
emit(EventContract.BottomTabSelected.EVENT, Arguments.createMap().apply {
|
|
65
|
+
putInt(EventContract.BottomTabSelected.SELECTED_TAB_INDEX, selectedTabIndex)
|
|
66
|
+
putInt(EventContract.BottomTabSelected.UNSELECTED_TAB_INDEX, unselectedTabIndex)
|
|
67
|
+
putString(EventContract.BottomTabSelected.BOTTOM_TABS_ID, bottomTabsId)
|
|
65
68
|
})
|
|
66
69
|
|
|
67
70
|
fun emitBottomTabPressed(tabIndex: Int, bottomTabsId: String, screenId: String?, wasSelected: Boolean) =
|
|
68
|
-
emit(
|
|
69
|
-
putInt(
|
|
70
|
-
putString(
|
|
71
|
-
screenId?.let { putString(
|
|
72
|
-
putBoolean(
|
|
71
|
+
emit(EventContract.BottomTabPressed.EVENT, Arguments.createMap().apply {
|
|
72
|
+
putInt(EventContract.BottomTabPressed.TAB_INDEX, tabIndex)
|
|
73
|
+
putString(EventContract.BottomTabPressed.BOTTOM_TABS_ID, bottomTabsId)
|
|
74
|
+
screenId?.let { putString(EventContract.BottomTabPressed.SCREEN_ID, it) }
|
|
75
|
+
putBoolean(EventContract.BottomTabPressed.WAS_SELECTED, wasSelected)
|
|
73
76
|
})
|
|
74
77
|
|
|
75
78
|
fun emitBottomTabLongPressed(selectedTabIndex: Int, bottomTabsId: String) =
|
|
76
|
-
emit(
|
|
77
|
-
putInt(
|
|
78
|
-
putString(
|
|
79
|
+
emit(EventContract.BottomTabLongPressed.EVENT, Arguments.createMap().apply {
|
|
80
|
+
putInt(EventContract.BottomTabLongPressed.SELECTED_TAB_INDEX, selectedTabIndex)
|
|
81
|
+
putString(EventContract.BottomTabLongPressed.BOTTOM_TABS_ID, bottomTabsId)
|
|
79
82
|
})
|
|
80
83
|
|
|
81
84
|
/** The id and the name (without `slot:`) of the screen the modal showed, and how many modals went. */
|
|
82
85
|
fun emitModalDismissed(componentId: String, componentName: String, modalsDismissed: Int) =
|
|
83
|
-
emit(
|
|
84
|
-
putString(
|
|
85
|
-
putInt(
|
|
86
|
+
emit(EventContract.ModalDismissed.EVENT, withId(componentId).apply {
|
|
87
|
+
putString(EventContract.ModalDismissed.COMPONENT_NAME, componentName)
|
|
88
|
+
putInt(EventContract.ModalDismissed.MODALS_DISMISSED, modalsDismissed)
|
|
86
89
|
})
|
|
87
90
|
|
|
88
|
-
fun emitModalAttemptedToDismiss(componentId: String) =
|
|
91
|
+
fun emitModalAttemptedToDismiss(componentId: String) =
|
|
92
|
+
emit(EventContract.ModalAttemptedToDismiss.EVENT, withId(componentId))
|
|
89
93
|
|
|
90
94
|
/** [detent]: `medium`, `large`, or a height in dp. */
|
|
91
95
|
fun emitSheetDetentChanged(componentId: String, detent: Any) =
|
|
92
|
-
emit(
|
|
96
|
+
emit(EventContract.SheetDetentChanged.EVENT, withId(componentId).apply {
|
|
93
97
|
when (detent) {
|
|
94
|
-
is Number -> putDouble(
|
|
95
|
-
else -> putString(
|
|
98
|
+
is Number -> putDouble(EventContract.SheetDetentChanged.DETENT, detent.toDouble())
|
|
99
|
+
else -> putString(EventContract.SheetDetentChanged.DETENT, detent.toString())
|
|
96
100
|
}
|
|
97
101
|
})
|
|
98
102
|
|
|
99
103
|
fun emitSearchBarUpdated(componentId: String, text: String, isFocused: Boolean, scope: String?) =
|
|
100
|
-
emit(
|
|
101
|
-
putString(
|
|
102
|
-
putBoolean(
|
|
103
|
-
scope?.let { putString(
|
|
104
|
+
emit(EventContract.SearchBarUpdated.EVENT, withId(componentId).apply {
|
|
105
|
+
putString(EventContract.SearchBarUpdated.TEXT, text)
|
|
106
|
+
putBoolean(EventContract.SearchBarUpdated.IS_FOCUSED, isFocused)
|
|
107
|
+
scope?.let { putString(EventContract.SearchBarUpdated.SCOPE, it) }
|
|
104
108
|
})
|
|
105
109
|
|
|
106
110
|
fun emitSearchBarSubmitted(componentId: String, text: String, scope: String?) =
|
|
107
|
-
emit(
|
|
108
|
-
putString(
|
|
109
|
-
scope?.let { putString(
|
|
111
|
+
emit(EventContract.SearchBarSubmitted.EVENT, withId(componentId).apply {
|
|
112
|
+
putString(EventContract.SearchBarSubmitted.TEXT, text)
|
|
113
|
+
scope?.let { putString(EventContract.SearchBarSubmitted.SCOPE, it) }
|
|
110
114
|
})
|
|
111
115
|
|
|
112
|
-
fun emitSearchBarCancelPressed(componentId: String) =
|
|
116
|
+
fun emitSearchBarCancelPressed(componentId: String) =
|
|
117
|
+
emit(EventContract.SearchBarCancelPressed.EVENT, withId(componentId))
|
|
113
118
|
|
|
114
119
|
fun emitSearchSuggestionPicked(componentId: String, suggestionId: String) =
|
|
115
|
-
emit(
|
|
120
|
+
emit(EventContract.SearchSuggestionPicked.EVENT, withId(componentId).apply {
|
|
121
|
+
putString(EventContract.SearchSuggestionPicked.SUGGESTION_ID, suggestionId)
|
|
122
|
+
})
|
|
116
123
|
|
|
117
124
|
fun emitSplitViewColumnsStacked(componentId: String, stacked: Boolean) =
|
|
118
|
-
emit(
|
|
125
|
+
emit(EventContract.SplitViewColumnsStacked.EVENT, withId(componentId).apply {
|
|
126
|
+
putBoolean(EventContract.SplitViewColumnsStacked.STACKED, stacked)
|
|
127
|
+
})
|
|
119
128
|
|
|
120
|
-
fun emitSlotDestroyed(componentId: String) = emit(
|
|
129
|
+
fun emitSlotDestroyed(componentId: String) = emit(EventContract.SlotDestroyed.EVENT, withId(componentId))
|
|
121
130
|
|
|
122
131
|
/** [insets]: `{ top, right, bottom, left }`. */
|
|
123
132
|
fun emitScreenInsetsChanged(componentId: String, insets: WritableMap) =
|
|
124
|
-
emit(
|
|
133
|
+
emit(EventContract.ScreenInsetsChanged.EVENT, withId(componentId).apply { merge(insets) })
|
|
125
134
|
|
|
126
135
|
/** [regions]: `{ division, occlusion }`. */
|
|
127
136
|
fun emitScreenRegionsChanged(componentId: String, regions: WritableMap) =
|
|
128
|
-
emit(
|
|
137
|
+
emit(EventContract.ScreenRegionsChanged.EVENT, withId(componentId).apply { merge(regions) })
|
|
129
138
|
|
|
130
139
|
/** [state]: `{ placement, layout, front, hidden }`. */
|
|
131
140
|
fun emitArrangementChanged(componentId: String, state: WritableMap) =
|
|
132
|
-
emit(
|
|
141
|
+
emit(EventContract.ArrangementChanged.EVENT, withId(componentId).apply { merge(state) })
|
|
133
142
|
|
|
134
143
|
/** [value]: `{ state, angle? }`, or `{}` without a hinge. */
|
|
135
|
-
fun emitHingeChanged(value: WritableMap) = emit(
|
|
144
|
+
fun emitHingeChanged(value: WritableMap) = emit(EventContract.HingeChanged.EVENT, value)
|
|
136
145
|
|
|
137
146
|
/** [kind]: `function` or `command`; [arguments] as JSON, which JavaScript gets as `args`. */
|
|
138
147
|
fun emitNativeScreenCall(componentId: String, callId: String, kind: String, name: String, arguments: JSONArray) =
|
|
139
|
-
emit(
|
|
140
|
-
putString(
|
|
141
|
-
putString(
|
|
142
|
-
putString(
|
|
143
|
-
putArray(
|
|
148
|
+
emit(EventContract.NativeScreenCall.EVENT, withId(componentId).apply {
|
|
149
|
+
putString(EventContract.NativeScreenCall.CALL_ID, callId)
|
|
150
|
+
putString(EventContract.NativeScreenCall.KIND, kind)
|
|
151
|
+
putString(EventContract.NativeScreenCall.NAME, name)
|
|
152
|
+
putArray(EventContract.NativeScreenCall.ARGS, NativeScreenCalls.toArguments(arguments))
|
|
144
153
|
})
|
|
145
154
|
|
|
146
|
-
fun emitNativeScreenReleased(componentId: String) =
|
|
155
|
+
fun emitNativeScreenReleased(componentId: String) =
|
|
156
|
+
emit(EventContract.NativeScreenReleased.EVENT, withId(componentId))
|
|
147
157
|
|
|
158
|
+
// The three appearance events have the same keys.
|
|
148
159
|
private fun component(componentId: String, componentName: String, type: ComponentType): WritableMap =
|
|
149
160
|
withId(componentId).apply {
|
|
150
|
-
putString(
|
|
151
|
-
putString(
|
|
161
|
+
putString(EventContract.ComponentDidAppear.COMPONENT_NAME, componentName)
|
|
162
|
+
putString(EventContract.ComponentDidAppear.COMPONENT_TYPE, type.jsName)
|
|
152
163
|
}
|
|
153
164
|
|
|
165
|
+
// The events of a screen carry its id under the same key.
|
|
154
166
|
private fun withId(componentId: String): WritableMap =
|
|
155
|
-
Arguments.createMap().apply { putString(
|
|
167
|
+
Arguments.createMap().apply { putString(EventContract.ScreenPopped.COMPONENT_ID, componentId) }
|
|
156
168
|
|
|
157
169
|
private fun emit(name: String, data: WritableMap) {
|
|
158
170
|
val context = reactContext
|
|
@@ -169,31 +181,33 @@ class EventEmitter(private val reactContext: ReactContext?) {
|
|
|
169
181
|
fun current(): EventEmitter =
|
|
170
182
|
EventEmitter(NavigationApplication.instanceOrNull?.reactGatewayOrNull?.currentReactContext)
|
|
171
183
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const val
|
|
175
|
-
const val
|
|
176
|
-
const val
|
|
177
|
-
const val
|
|
178
|
-
const val
|
|
179
|
-
const val
|
|
180
|
-
const val
|
|
181
|
-
const val
|
|
182
|
-
const val
|
|
183
|
-
const val
|
|
184
|
-
const val
|
|
185
|
-
const val
|
|
186
|
-
const val
|
|
187
|
-
const val
|
|
188
|
-
const val
|
|
189
|
-
const val
|
|
190
|
-
const val
|
|
191
|
-
const val
|
|
192
|
-
const val
|
|
193
|
-
const val
|
|
194
|
-
const val
|
|
195
|
-
const val
|
|
196
|
-
const val
|
|
197
|
-
const val
|
|
184
|
+
// The names of the events, for modules outside the library; inside it, [EventContract] has them with their
|
|
185
|
+
// keys.
|
|
186
|
+
const val APP_LAUNCHED = EventContract.AppLaunched.EVENT
|
|
187
|
+
const val COMMAND_COMPLETED = EventContract.CommandCompleted.EVENT
|
|
188
|
+
const val COMPONENT_WILL_APPEAR = EventContract.ComponentWillAppear.EVENT
|
|
189
|
+
const val COMPONENT_DID_APPEAR = EventContract.ComponentDidAppear.EVENT
|
|
190
|
+
const val COMPONENT_DID_DISAPPEAR = EventContract.ComponentDidDisappear.EVENT
|
|
191
|
+
const val NAVIGATION_STATE_CHANGED = EventContract.NavigationStateChanged.EVENT
|
|
192
|
+
const val SCREEN_POPPED = EventContract.ScreenPopped.EVENT
|
|
193
|
+
const val NAVIGATION_BUTTON_PRESSED = EventContract.NavigationButtonPressed.EVENT
|
|
194
|
+
const val BOTTOM_TAB_SELECTED = EventContract.BottomTabSelected.EVENT
|
|
195
|
+
const val BOTTOM_TAB_PRESSED = EventContract.BottomTabPressed.EVENT
|
|
196
|
+
const val BOTTOM_TAB_LONG_PRESSED = EventContract.BottomTabLongPressed.EVENT
|
|
197
|
+
const val MODAL_DISMISSED = EventContract.ModalDismissed.EVENT
|
|
198
|
+
const val MODAL_ATTEMPTED_TO_DISMISS = EventContract.ModalAttemptedToDismiss.EVENT
|
|
199
|
+
const val SHEET_DETENT_CHANGED = EventContract.SheetDetentChanged.EVENT
|
|
200
|
+
const val SEARCH_BAR_UPDATED = EventContract.SearchBarUpdated.EVENT
|
|
201
|
+
const val SEARCH_BAR_SUBMITTED = EventContract.SearchBarSubmitted.EVENT
|
|
202
|
+
const val SEARCH_BAR_CANCEL_PRESSED = EventContract.SearchBarCancelPressed.EVENT
|
|
203
|
+
const val SEARCH_SUGGESTION_PICKED = EventContract.SearchSuggestionPicked.EVENT
|
|
204
|
+
const val SPLIT_VIEW_COLUMNS_STACKED = EventContract.SplitViewColumnsStacked.EVENT
|
|
205
|
+
const val SLOT_DESTROYED = EventContract.SlotDestroyed.EVENT
|
|
206
|
+
const val SCREEN_INSETS_CHANGED = EventContract.ScreenInsetsChanged.EVENT
|
|
207
|
+
const val SCREEN_REGIONS_CHANGED = EventContract.ScreenRegionsChanged.EVENT
|
|
208
|
+
const val ARRANGEMENT_CHANGED = EventContract.ArrangementChanged.EVENT
|
|
209
|
+
const val HINGE_CHANGED = EventContract.HingeChanged.EVENT
|
|
210
|
+
const val NATIVE_SCREEN_CALL = EventContract.NativeScreenCall.EVENT
|
|
211
|
+
const val NATIVE_SCREEN_RELEASED = EventContract.NativeScreenReleased.EVENT
|
|
198
212
|
}
|
|
199
213
|
}
|
|
@@ -45,12 +45,6 @@ internal class ScreenAppearance(
|
|
|
45
45
|
/** Cancels the hold limit running for [held]; none when nothing is held. */
|
|
46
46
|
internal var cancelLimit: (() -> Unit)? = null
|
|
47
47
|
|
|
48
|
-
/**
|
|
49
|
-
* This appearance started without its WillAppear (`AppearanceDispatcher.withoutWillAppear`: a side menu's drawer
|
|
50
|
-
* that jumped open); its DidAppear is its first event. Cleared as it is hidden.
|
|
51
|
-
*/
|
|
52
|
-
internal var skippedWill = false
|
|
53
|
-
|
|
54
48
|
private var ready = content == null
|
|
55
49
|
private var waitsForContent = false
|
|
56
50
|
|
|
@@ -22,8 +22,8 @@ object UiUtils {
|
|
|
22
22
|
fun pxToDp(context: Context, px: Float): Float = px / context.resources.displayMetrics.density
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* A size of the options in dp, in whole pixels of the screen of [context]: times `densityDpi / 160`, the
|
|
26
|
-
*
|
|
25
|
+
* A size of the options in dp, in whole pixels of the screen of [context]: times `densityDpi / 160`, rounded to the
|
|
26
|
+
* nearest pixel only here, as iOS places a size in points on whole pixels; a size of 0 or less is not converted.
|
|
27
27
|
*/
|
|
28
28
|
@JvmStatic
|
|
29
29
|
fun dpToPixels(context: Context, dp: Double): Int = dpToPixels(dp, context.resources.displayMetrics.densityDpi)
|
|
@@ -31,7 +31,7 @@ object UiUtils {
|
|
|
31
31
|
/** [dp] in whole pixels at [densityDpi], as [dpToPixels] converts it. */
|
|
32
32
|
@JvmStatic
|
|
33
33
|
fun dpToPixels(dp: Double, densityDpi: Int): Int =
|
|
34
|
-
if (dp <= 0) dp.toInt() else (dp * densityDpi / BASE_DENSITY_DPI).
|
|
34
|
+
if (dp <= 0) dp.toInt() else (dp * densityDpi / BASE_DENSITY_DPI).roundToInt()
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* A font size of the options in sp, in pixels of the screen of [context]: it follows the system's text scale,
|
|
@@ -2,24 +2,25 @@ package dev.apexnative.navigation.viewcontrollers.arrangement
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import dev.apexnative.navigation.options.Options
|
|
5
|
+
import dev.apexnative.navigation.viewcontrollers.component.ComponentPresenter
|
|
5
6
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
6
7
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The presenter of the secondary screen of a pair ([ArrangementController]). The status bar, the navigation bar and
|
|
10
|
-
* the orientation are the primary's, as the top bar is: the secondary applies only its background
|
|
11
|
-
* front it gives them back to the primary.
|
|
11
|
+
* the orientation are the primary's, as the top bar is: the secondary applies only its background (the theme's
|
|
12
|
+
* without `layout.backgroundColor`, as any React screen), and brought to the front it gives them back to the primary.
|
|
12
13
|
*/
|
|
13
14
|
internal class SecondaryScreenPresenter(activity: Activity, defaultOptions: Options) :
|
|
14
15
|
Presenter(activity, defaultOptions) {
|
|
15
16
|
|
|
16
17
|
override fun applyOptions(view: ViewController<*>, options: Options) =
|
|
17
|
-
|
|
18
|
+
applyBackground(view, options.copy().withDefaultOptions(defaultOptions))
|
|
18
19
|
|
|
19
20
|
override fun mergeOptions(viewController: ViewController<*>, options: Options) {
|
|
20
21
|
if (!options.layout.backgroundColor.hasValue()) return
|
|
21
22
|
val merged = viewController.resolveCurrentOptions().copy().mergeWith(options)
|
|
22
|
-
|
|
23
|
+
applyBackground(viewController, merged.withDefaultOptions(defaultOptions))
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
override fun onViewBroughtToFront(viewController: ViewController<*>, options: Options) {
|
|
@@ -27,5 +28,12 @@ internal class SecondaryScreenPresenter(activity: Activity, defaultOptions: Opti
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
override fun onConfigurationChanged(controller: ViewController<*>, options: Options) =
|
|
30
|
-
|
|
31
|
+
applyBackground(controller, options.copy().withDefaultOptions(defaultOptions))
|
|
32
|
+
|
|
33
|
+
// `layout.backgroundColor`, else the theme's background (`ComponentPresenter.themeBackgroundOf`).
|
|
34
|
+
private fun applyBackground(view: ViewController<*>, options: Options) {
|
|
35
|
+
if (options.layout.backgroundColor.hasValue()) return applyBackgroundColor(view, options)
|
|
36
|
+
val color = ComponentPresenter.themeBackgroundOf(activity) ?: return
|
|
37
|
+
view.peekView()?.setBackgroundColor(color)
|
|
38
|
+
}
|
|
31
39
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
package dev.apexnative.navigation.viewcontrollers.bottomtabs
|
|
2
2
|
|
|
3
|
+
import android.animation.Animator
|
|
4
|
+
import android.animation.AnimatorListenerAdapter
|
|
5
|
+
import android.animation.AnimatorSet
|
|
3
6
|
import android.app.Activity
|
|
4
7
|
import android.graphics.Color
|
|
5
8
|
import android.util.Log
|
|
@@ -33,6 +36,7 @@ import dev.apexnative.navigation.viewcontrollers.stack.fab.FabPlacement
|
|
|
33
36
|
import dev.apexnative.navigation.viewcontrollers.stack.topbar.BarComponents
|
|
34
37
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
35
38
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
39
|
+
import dev.apexnative.navigation.views.animations.FadeThrough
|
|
36
40
|
import dev.apexnative.navigation.views.bottomtabs.BottomTabItem
|
|
37
41
|
import dev.apexnative.navigation.views.bottomtabs.BottomTabsLayout
|
|
38
42
|
import dev.apexnative.navigation.views.bottomtabs.CustomBottomTabItemView
|
|
@@ -68,6 +72,8 @@ class BottomTabsController(
|
|
|
68
72
|
private var cells: List<CustomBottomTabItemView> = emptyList()
|
|
69
73
|
// `ANN.BottomTabSelected` of a press that waits for the new tab's screen to appear.
|
|
70
74
|
private var pendingSelected: (() -> Unit)? = null
|
|
75
|
+
// Ends the fade through of a switch still running, with its events.
|
|
76
|
+
private var endSwitch: (() -> Unit)? = null
|
|
71
77
|
// The bar goes with a stack's transition: the options of the new top don't move it themselves.
|
|
72
78
|
private var riding = false
|
|
73
79
|
private var restAttached = false
|
|
@@ -150,8 +156,11 @@ class BottomTabsController(
|
|
|
150
156
|
/** The view of [tab] joins, or is there already: [visible], or invisible but laid out. */
|
|
151
157
|
fun attach(tab: ViewController<*>, visible: Boolean)
|
|
152
158
|
|
|
153
|
-
/**
|
|
154
|
-
|
|
159
|
+
/**
|
|
160
|
+
* The view of [shown] becomes visible and that of [hidden] invisible: at once, or with Material's fade through
|
|
161
|
+
* when [animated]; then [onEnd]. The function returned ends a switch still running at once, [onEnd] included.
|
|
162
|
+
*/
|
|
163
|
+
fun switch(shown: ViewController<*>, hidden: ViewController<*>, animated: Boolean, onEnd: () -> Unit): () -> Unit
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
/** The views of the tabs; none before the tabs' view is made. */
|
|
@@ -166,9 +175,43 @@ class BottomTabsController(
|
|
|
166
175
|
tab.view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
|
|
167
176
|
}
|
|
168
177
|
|
|
169
|
-
override fun switch(
|
|
170
|
-
shown
|
|
171
|
-
hidden
|
|
178
|
+
override fun switch(
|
|
179
|
+
shown: ViewController<*>,
|
|
180
|
+
hidden: ViewController<*>,
|
|
181
|
+
animated: Boolean,
|
|
182
|
+
onEnd: () -> Unit,
|
|
183
|
+
): () -> Unit {
|
|
184
|
+
val coming = shown.view
|
|
185
|
+
coming.visibility = View.VISIBLE
|
|
186
|
+
val going = hidden.peekView()
|
|
187
|
+
if (!animated || going == null) {
|
|
188
|
+
going?.visibility = View.INVISIBLE
|
|
189
|
+
onEnd()
|
|
190
|
+
return {}
|
|
191
|
+
}
|
|
192
|
+
val exit = FadeThrough.exit(going)
|
|
193
|
+
// Faded out, the tab that goes takes no more touches and no accessibility focus.
|
|
194
|
+
exit.addListener(object : AnimatorListenerAdapter() {
|
|
195
|
+
override fun onAnimationEnd(animation: Animator) {
|
|
196
|
+
going.visibility = View.INVISIBLE
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
val set = AnimatorSet()
|
|
200
|
+
set.playTogether(exit, FadeThrough.enter(coming))
|
|
201
|
+
set.addListener(object : AnimatorListenerAdapter() {
|
|
202
|
+
override fun onAnimationEnd(animation: Animator) {
|
|
203
|
+
set.removeAllListeners()
|
|
204
|
+
// The tab that went keeps its look for its next selection; only its visibility hides it.
|
|
205
|
+
going.visibility = View.INVISIBLE
|
|
206
|
+
going.alpha = 1f
|
|
207
|
+
coming.alpha = 1f
|
|
208
|
+
coming.scaleX = 1f
|
|
209
|
+
coming.scaleY = 1f
|
|
210
|
+
onEnd()
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
set.start()
|
|
214
|
+
return set::end
|
|
172
215
|
}
|
|
173
216
|
}
|
|
174
217
|
|
|
@@ -302,10 +345,9 @@ class BottomTabsController(
|
|
|
302
345
|
}
|
|
303
346
|
|
|
304
347
|
/**
|
|
305
|
-
* Selects the tab at [index]:
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* and the state at once. The history of back follows.
|
|
348
|
+
* Selects the tab at [index]: the bar shows it at once; on screen the tab it left fades through to it (Material's
|
|
349
|
+
* fade through), off screen one container becomes invisible and the other visible at once; then [switched]. The
|
|
350
|
+
* history of back follows.
|
|
309
351
|
*/
|
|
310
352
|
private fun select(index: Int, how: How) {
|
|
311
353
|
if (index == selectedIndex || index !in tabs.indices || isDestroyed) return
|
|
@@ -328,10 +370,27 @@ class BottomTabsController(
|
|
|
328
370
|
// A layer that waits for the tabs waits for the tab they show now.
|
|
329
371
|
navigator?.shownScreensChanged(this)
|
|
330
372
|
if (onScreen) AppearanceDispatcher.beginTransition(listOf(shown))
|
|
331
|
-
views
|
|
373
|
+
if (views == null) return switched(index, previous, how, onScreen, hasViews = false)
|
|
374
|
+
endSwitch = views.switch(shown, left, animated = onScreen) {
|
|
375
|
+
endSwitch = null
|
|
376
|
+
if (!isDestroyed) switched(index, previous, how, onScreen, hasViews = true)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* The switch to the tab at [index] from [previous] ended: what went disappears, what came appears — unless the tabs
|
|
382
|
+
* stopped showing during the fade (a modal over them): it appears when they show again; then for a press
|
|
383
|
+
* `ANN.BottomTabSelected` right after the new screen's DidAppear and the left tab's `popToRootOnBlur`, with the state
|
|
384
|
+
* as one update after them; from code and by back, `popToRootOnBlur` and the state at once. [onScreen]: the switch
|
|
385
|
+
* started on screen.
|
|
386
|
+
*/
|
|
387
|
+
private fun switched(index: Int, previous: Int, how: How, onScreen: Boolean, hasViews: Boolean) {
|
|
388
|
+
val left = tabs[previous]
|
|
389
|
+
val shown = tabs[index]
|
|
332
390
|
if (onScreen) {
|
|
333
|
-
|
|
334
|
-
|
|
391
|
+
val showing = if (isOnScreen) listOf(shown) else emptyList()
|
|
392
|
+
AppearanceDispatcher.endTransition(listOf(left), showing, null, null, null, reportsState = false)
|
|
393
|
+
} else if (hasViews) {
|
|
335
394
|
// Not on screen: the bar takes the new tab's options as they are, without appearance events.
|
|
336
395
|
val options = resolveShownOptions(DefaultOptions.current)
|
|
337
396
|
bar.applyBar(options.bottomTabs, directionOf(options))
|
|
@@ -358,8 +417,10 @@ class BottomTabsController(
|
|
|
358
417
|
if (screen == null) finish() else AppearanceDispatcher.afterDidAppear(screen, finish)
|
|
359
418
|
}
|
|
360
419
|
|
|
361
|
-
// A press's `ANN.BottomTabSelected` still waiting goes before anything
|
|
420
|
+
// A switch still running ends at once and a press's `ANN.BottomTabSelected` still waiting goes, before anything
|
|
421
|
+
// newer on these tabs.
|
|
362
422
|
private fun flushPendingSelected() {
|
|
423
|
+
endSwitch?.invoke()
|
|
363
424
|
pendingSelected?.invoke()
|
|
364
425
|
}
|
|
365
426
|
|
|
@@ -621,6 +682,8 @@ class BottomTabsController(
|
|
|
621
682
|
if (isDestroyed) return
|
|
622
683
|
pendingSelected = null
|
|
623
684
|
super.destroy()
|
|
685
|
+
// A switch still running ends with its views at rest, without events: the tabs are gone.
|
|
686
|
+
endSwitch?.invoke()
|
|
624
687
|
peekView()?.let { layout ->
|
|
625
688
|
layout.bar.destroyCustomItemViews()
|
|
626
689
|
layout.row?.let(CustomTabRowPlacer::forget)
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt
CHANGED
|
@@ -36,7 +36,7 @@ internal class TabItems(private val color: (ThemeColour) -> Int?) {
|
|
|
36
36
|
) : Call()
|
|
37
37
|
|
|
38
38
|
/** The icon's size in dp, width over height. */
|
|
39
|
-
data class IconSize(override val index: Int, val width:
|
|
39
|
+
data class IconSize(override val index: Int, val width: Double?, val height: Double?) : Call()
|
|
40
40
|
|
|
41
41
|
data class Font(override val index: Int, val font: FontOptions) : Call()
|
|
42
42
|
|
|
@@ -86,7 +86,7 @@ internal class TabItems(private val color: (ThemeColour) -> Int?) {
|
|
|
86
86
|
options.icon.get(null)?.let { Call.Icon(index, it) },
|
|
87
87
|
options.selectedIcon.get(null)?.let { Call.SelectedIcon(index, it) },
|
|
88
88
|
colors(index, options),
|
|
89
|
-
Call.IconSize(index, options.iconWidth.get(null)
|
|
89
|
+
Call.IconSize(index, options.iconWidth.get(null), options.iconHeight.get(null)),
|
|
90
90
|
Call.Font(index, options.font.copy()),
|
|
91
91
|
Call.FontSizes(index, options.selectedFontSize.get(null)?.toFloat(), options.fontSize.get(null)?.toFloat()),
|
|
92
92
|
Call.TestId(index, options.testID.get(null)),
|
|
@@ -110,7 +110,7 @@ internal class TabItems(private val color: (ThemeColour) -> Int?) {
|
|
|
110
110
|
calls += colors(index, resolved)
|
|
111
111
|
}
|
|
112
112
|
if (merge.iconWidth.hasValue() || merge.iconHeight.hasValue()) {
|
|
113
|
-
calls += Call.IconSize(index, resolved.iconWidth.get(null)
|
|
113
|
+
calls += Call.IconSize(index, resolved.iconWidth.get(null), resolved.iconHeight.get(null))
|
|
114
114
|
}
|
|
115
115
|
if (merge.font.hasValue()) calls += Call.Font(index, resolved.font.copy())
|
|
116
116
|
if (merge.fontSize.hasValue() || merge.selectedFontSize.hasValue()) {
|
|
@@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable
|
|
|
6
6
|
import android.graphics.drawable.Drawable
|
|
7
7
|
import android.graphics.drawable.LayerDrawable
|
|
8
8
|
import android.view.View
|
|
9
|
+
import com.google.android.material.color.MaterialColors
|
|
9
10
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
10
11
|
import dev.apexnative.navigation.options.Options
|
|
11
12
|
import dev.apexnative.navigation.options.params.ThemeColour
|
|
@@ -15,9 +16,9 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
|
15
16
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* The presenter of a screen, React or native: its background — the screen's view takes `layout.backgroundColor
|
|
19
|
-
* out of the status bar's height where the status bar shows over
|
|
20
|
-
* `layout.componentBackgroundColor` over it —, its room for the bars, and its safe area again on `mergeOptions`
|
|
19
|
+
* The presenter of a screen, React or native: its background — the screen's view takes `layout.backgroundColor` (a
|
|
20
|
+
* React screen without it the theme's background), kept out of the status bar's height where the status bar shows over
|
|
21
|
+
* it, and the React content takes `layout.componentBackgroundColor` over it —, its room for the bars, and its safe area again on `mergeOptions`
|
|
21
22
|
* (`mergeOptions` step 4). It applies as the screen appears, on `mergeOptions` of what it covers, and again with new
|
|
22
23
|
* default options or after a light/dark switch from the resolved options it is given, which it never writes. Each
|
|
23
24
|
* appearance tells the activity's system UI owner, which follows the screen on top. Main thread only.
|
|
@@ -49,8 +50,8 @@ open class ComponentPresenter(activity: Activity) : Presenter(activity, DefaultO
|
|
|
49
50
|
applyBackground(controller, options)
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
|
-
* The colors of [screen] from its resolved [options]: `layout.backgroundColor` on the screen's view,
|
|
53
|
-
*
|
|
53
|
+
* The colors of [screen] from its resolved [options]: `layout.backgroundColor` on the screen's view, without it the
|
|
54
|
+
* theme's background for a React screen ([defaultColorOf]); `layout.componentBackgroundColor` on its React content.
|
|
54
55
|
*/
|
|
55
56
|
fun applyBackground(screen: ViewController<*>, options: Options) {
|
|
56
57
|
val view = screen.peekView() ?: return
|
|
@@ -59,11 +60,11 @@ open class ComponentPresenter(activity: Activity) : Presenter(activity, DefaultO
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
private fun screenBackground(screen: ViewController<*>, options: Options): Drawable? {
|
|
62
|
-
val color = colorOf(options.layout.backgroundColor) ?: return null
|
|
63
|
+
val color = colorOf(options.layout.backgroundColor) ?: defaultColorOf(screen) ?: return null
|
|
63
64
|
val frame = frameOf(screen) ?: return ColorDrawable(color)
|
|
64
65
|
val band = ScreenSafeArea.backgroundBand(
|
|
65
66
|
statusBarHeight = frame.statusBarHeight,
|
|
66
|
-
hiddenOrDrawBehind = options.statusBar.isHiddenOrDrawBehind,
|
|
67
|
+
hiddenOrDrawBehind = options.statusBar.isHiddenOrDrawBehind(screen.isInOverlay),
|
|
67
68
|
inSideMenu = screen.isInSideMenu,
|
|
68
69
|
inSheet = screen.isInSheet,
|
|
69
70
|
inPair = frame.isInPair,
|
|
@@ -79,6 +80,15 @@ open class ComponentPresenter(activity: Activity) : Presenter(activity, DefaultO
|
|
|
79
80
|
private fun colorOf(colour: ThemeColour): Int? =
|
|
80
81
|
if (colour.hasValue()) colour.get(activity, Color.TRANSPARENT) ?: Color.TRANSPARENT else null
|
|
81
82
|
|
|
83
|
+
// A React screen is opaque without a color of its own, as iOS gives it the system background: the screen under it
|
|
84
|
+
// never shows through, also while a push slides it aside. The app theme's `android:colorBackground`, read now, so a
|
|
85
|
+
// light/dark switch takes the new mode's. A screen of a sheet shows the sheet's surface; a native screen paints its
|
|
86
|
+
// own view.
|
|
87
|
+
private fun defaultColorOf(screen: ViewController<*>): Int? {
|
|
88
|
+
if (screen !is ComponentViewController || screen.isInSheet) return null
|
|
89
|
+
return themeBackgroundOf(activity)
|
|
90
|
+
}
|
|
91
|
+
|
|
82
92
|
private fun frameOf(screen: ViewController<*>): ScreenFrame? = when (screen) {
|
|
83
93
|
is ComponentViewController -> screen.frame
|
|
84
94
|
is ExternalComponentViewController -> screen.frame
|
|
@@ -87,4 +97,15 @@ open class ComponentPresenter(activity: Activity) : Presenter(activity, DefaultO
|
|
|
87
97
|
|
|
88
98
|
// The view under the React content: the React root, or the container of a slot screen. A native screen has none.
|
|
89
99
|
private fun contentOf(screen: ViewController<*>): View? = (screen as? ComponentViewController)?.content?.asView()
|
|
100
|
+
|
|
101
|
+
internal companion object {
|
|
102
|
+
/**
|
|
103
|
+
* The background of a React screen without `layout.backgroundColor`: `android:colorBackground` of [activity]'s
|
|
104
|
+
* theme now. None for an activity without a theme (the unit tests' fakes).
|
|
105
|
+
*/
|
|
106
|
+
fun themeBackgroundOf(activity: Activity): Int? {
|
|
107
|
+
if (activity.theme == null) return null
|
|
108
|
+
return MaterialColors.getColor(activity, android.R.attr.colorBackground, Color.WHITE)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
90
111
|
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt
CHANGED
|
@@ -143,7 +143,7 @@ internal class ScreenFrame(
|
|
|
143
143
|
val parent = node.parentController
|
|
144
144
|
val statusBar = ScreenSafeArea.statusBarRoom(
|
|
145
145
|
statusBarHeight = statusBarHeight,
|
|
146
|
-
hiddenOrDrawBehind = options.statusBar.isHiddenOrDrawBehind,
|
|
146
|
+
hiddenOrDrawBehind = options.statusBar.isHiddenOrDrawBehind(node.isInOverlay),
|
|
147
147
|
inSheet = node.isInSheet,
|
|
148
148
|
inSideMenu = node.isInSideMenu,
|
|
149
149
|
keptAround = parent?.keepsStatusBarRoomOf(node) == true,
|