@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.2
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 +16 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +10 -4
- package/android/CORE.md +4 -1
- package/android/RECIPES.md +4 -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/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +12 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +12 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -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 +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +2 -1
- 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/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 +1 -1
- package/docs-md/guides/deep-links.md +15 -33
- 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/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/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 +17 -13
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +15 -11
- 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 +4 -3
- 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/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 +17 -13
- package/src/interfaces/options/modals.ts +15 -11
- package/src/interfaces/options/sideMenu.ts +7 -6
- package/src/interfaces/options/system.ts +4 -3
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()) {
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt
CHANGED
|
@@ -147,8 +147,11 @@ internal class ModalCoordinator(
|
|
|
147
147
|
*/
|
|
148
148
|
fun carry(record: ModalRecord, oldRoot: ViewController<*>?)
|
|
149
149
|
|
|
150
|
-
/**
|
|
151
|
-
|
|
150
|
+
/**
|
|
151
|
+
* New [options] (resolved) of the record: its `testID`s, its sheet's `update`; [merge] is the `mergeOptions`
|
|
152
|
+
* that brought them, which alone may move the sheet, or null when the shown screen changed.
|
|
153
|
+
*/
|
|
154
|
+
fun update(record: ModalRecord, options: Options, merge: Options?)
|
|
152
155
|
|
|
153
156
|
/** The colors of the record's own views again (a light/dark switch). */
|
|
154
157
|
fun repaint(record: ModalRecord) = Unit
|
|
@@ -353,13 +356,14 @@ internal class ModalCoordinator(
|
|
|
353
356
|
// Options.
|
|
354
357
|
|
|
355
358
|
/**
|
|
356
|
-
* A `mergeOptions` went into [node] (`mergeOptions` step 5): when it is in a modal, the modal's sheet
|
|
357
|
-
* options again
|
|
359
|
+
* A `mergeOptions` of [merge] went into [node] (`mergeOptions` step 5): when it is in a modal, the modal's sheet
|
|
360
|
+
* takes its resolved options again, moving only to a `selectedDetent` that [merge] names, and its `testID`s apply
|
|
361
|
+
* again.
|
|
358
362
|
*/
|
|
359
|
-
fun optionsMergedInto(node: ViewController
|
|
363
|
+
fun optionsMergedInto(node: ViewController<*>, merge: Options) {
|
|
360
364
|
val record = recordOf(node) ?: return
|
|
361
365
|
if (record.root.isDestroyed) return
|
|
362
|
-
views.update(record, record.root.resolveShownOptions(DefaultOptions.current),
|
|
366
|
+
views.update(record, record.root.resolveShownOptions(DefaultOptions.current), merge)
|
|
363
367
|
}
|
|
364
368
|
|
|
365
369
|
/**
|
|
@@ -369,7 +373,7 @@ internal class ModalCoordinator(
|
|
|
369
373
|
fun visibleScreenChanged(node: ViewController<*>) {
|
|
370
374
|
val record = recordOf(node) ?: return
|
|
371
375
|
if (record.state == State.Waiting || record.root.isDestroyed) return
|
|
372
|
-
views.update(record, record.root.resolveShownOptions(DefaultOptions.current),
|
|
376
|
+
views.update(record, record.root.resolveShownOptions(DefaultOptions.current), merge = null)
|
|
373
377
|
}
|
|
374
378
|
|
|
375
379
|
/** The colors of the modals' own views again, after a light/dark switch. */
|
|
@@ -1230,7 +1234,7 @@ internal class ModalCoordinator(
|
|
|
1230
1234
|
val screen = AppearanceDispatcher.visibleNode(record.root)
|
|
1231
1235
|
if (screen.isDestroyed) return
|
|
1232
1236
|
screen.mergeOptions(options)
|
|
1233
|
-
optionsMergedInto(screen)
|
|
1237
|
+
optionsMergedInto(screen, options)
|
|
1234
1238
|
}
|
|
1235
1239
|
|
|
1236
1240
|
private inline fun whileTaking(work: () -> Unit) {
|
|
@@ -122,31 +122,32 @@ internal class ModalSheet(
|
|
|
122
122
|
// where the sheet is laid out.
|
|
123
123
|
layout.addView(container, CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT).also { it.behavior = behavior })
|
|
124
124
|
shown = onShown
|
|
125
|
-
update(sheetOptions, swipeToDismiss,
|
|
125
|
+
update(sheetOptions, swipeToDismiss, merge = null)
|
|
126
|
+
selected = sheetOptions.selectedDetent?.takeIf { it in detents }
|
|
126
127
|
// Laid out hidden first, so it slides up from the bottom.
|
|
127
128
|
container.post { if (closing == null) behavior.state = stateOf(selected ?: detents.first()) }
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
/**
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
/**
|
|
132
|
+
* New resolved options: the detents and the look. The sheet moves only as [SheetStops] says, after a `mergeOptions`
|
|
133
|
+
* of [merge]; without one (the shown screen changed, the show) it stays where it is.
|
|
134
|
+
*/
|
|
135
|
+
fun update(sheetOptions: SheetOptions, swipeToDismiss: Boolean, merge: SheetOptions?) {
|
|
133
136
|
options = sheetOptions
|
|
134
137
|
this.swipeToDismiss = swipeToDismiss
|
|
135
138
|
detents = sheetOptions.detents?.takeIf { it.isNotEmpty() }?.sortedBy { heightOf(it) } ?: listOf(SheetDetent.Large)
|
|
136
|
-
selected = sheetOptions.selectedDetent?.takeIf { it in detents }
|
|
137
139
|
grabber.visibility = if (sheetOptions.grabber.isTrue) View.VISIBLE else View.GONE
|
|
138
140
|
behavior.dragAnywhere = sheetOptions.expandsOnScroll.get(true)
|
|
139
141
|
container.invalidateOutline()
|
|
140
142
|
configure()
|
|
141
|
-
if (
|
|
143
|
+
if (merge == null || closing != null) return
|
|
142
144
|
val settled = detentOf(behavior.state)
|
|
143
145
|
if (settled != null) behavior.isHideable = swipeToDismiss && settled == detents.first()
|
|
144
|
-
val target =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
val target = SheetStops.target(settled, detents, merge.selectedDetent, sheetOptions.selectedDetent)
|
|
147
|
+
if (target != null) {
|
|
148
|
+
selected = target
|
|
149
|
+
behavior.state = stateOf(target)
|
|
148
150
|
}
|
|
149
|
-
if (target != null) behavior.state = stateOf(target)
|
|
150
151
|
updateScrim()
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -245,10 +245,10 @@ internal class ModalViews(
|
|
|
245
245
|
hideEmptyLayer()
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
override fun update(record: ModalRecord, options: Options,
|
|
248
|
+
override fun update(record: ModalRecord, options: Options, merge: Options?) {
|
|
249
249
|
val parts = parts[record] ?: return
|
|
250
250
|
applyTestIds(record, parts, options)
|
|
251
|
-
parts.sheet?.update(options.modal.sheet, swipeToDismissOf(options),
|
|
251
|
+
parts.sheet?.update(options.modal.sheet, swipeToDismissOf(options), merge?.modal?.sheet)
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
override fun repaint(record: ModalRecord) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package dev.apexnative.navigation.viewcontrollers.modal
|
|
2
|
+
|
|
3
|
+
import dev.apexnative.navigation.options.SheetDetent
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Where a shown sheet goes at new options, as on iOS: the sheet keeps the stop it stands at (the user's last drag
|
|
7
|
+
* included) until a `selectedDetent` arrives in a `mergeOptions`, or until its stop is no longer among the detents.
|
|
8
|
+
*/
|
|
9
|
+
internal object SheetStops {
|
|
10
|
+
/**
|
|
11
|
+
* The stop to move to, or null to stay: [arrived] (the `selectedDetent` the merge names) when it is among
|
|
12
|
+
* [detents]; else, when [settled] (the stop the sheet rests at) is no longer one of them, [selected] (the resolved
|
|
13
|
+
* `selectedDetent`) when it is, or the lowest.
|
|
14
|
+
*/
|
|
15
|
+
fun target(
|
|
16
|
+
settled: SheetDetent?,
|
|
17
|
+
detents: List<SheetDetent>,
|
|
18
|
+
arrived: SheetDetent?,
|
|
19
|
+
selected: SheetDetent?,
|
|
20
|
+
): SheetDetent? = when {
|
|
21
|
+
arrived != null && arrived in detents -> arrived
|
|
22
|
+
settled != null && settled !in detents -> selected?.takeIf { it in detents } ?: detents.first()
|
|
23
|
+
else -> null
|
|
24
|
+
}
|
|
25
|
+
}
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt
CHANGED
|
@@ -214,7 +214,7 @@ class Navigator(
|
|
|
214
214
|
* it itself, `ComponentPresenter`); the system UI follows (only what changed is written).
|
|
215
215
|
*/
|
|
216
216
|
internal fun optionsMerged(node: ViewController<*>, options: Options = Options()) {
|
|
217
|
-
modals.optionsMergedInto(node)
|
|
217
|
+
modals.optionsMergedInto(node, options)
|
|
218
218
|
overlays.optionsMergedInto(node)
|
|
219
219
|
val navigationBar = options.navigationBar
|
|
220
220
|
val padding = navigationBar.drawBehind.hasValue() || navigationBar.backgroundColor.hasValue()
|
|
@@ -255,9 +255,7 @@ internal class SideMenuController(
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
// The system UI follows the screen the user sees now, at rest: the open menu's, the center's once a close
|
|
258
|
-
// ends. The owner reads it before the next frame, when a menu that closed is hidden already.
|
|
259
|
-
// animation runs this inside `AppearanceDispatcher.withoutWillAppear`: what starts to appear from here would
|
|
260
|
-
// send no WillAppear.
|
|
258
|
+
// ends. The owner reads it before the next frame, when a menu that closed is hidden already.
|
|
261
259
|
override fun shownChanged() {
|
|
262
260
|
navigator?.shownScreensChanged(this@SideMenuController)
|
|
263
261
|
navigator?.systemUi?.invalidate()
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt
CHANGED
|
@@ -16,8 +16,8 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
|
16
16
|
* the drawer calls `onDrawerClosed` only for a drawer that had opened.
|
|
17
17
|
* - The menu screen's events: WillAppear at the side's first movement, DidAppear when it opened, DidDisappear when it
|
|
18
18
|
* rests closed; only while the side menu has appeared ([Host.appeared]) — otherwise they come with the side menu's
|
|
19
|
-
* own appearance (its visible children). An open without animation sends
|
|
20
|
-
*
|
|
19
|
+
* own appearance (its visible children). An open without animation sends both too, WillAppear first, as on iOS:
|
|
20
|
+
* the drawer jumps to its open offset before it calls the open.
|
|
21
21
|
* - At each rest: one state update after the menu's events, the back callback, and the layers that follow the shown
|
|
22
22
|
* screen.
|
|
23
23
|
* - A drawer that doesn't call back (not laid out since it joined the window, or not in one: `DrawerLayout` only
|
|
@@ -246,11 +246,8 @@ internal class SideMenuMotion(
|
|
|
246
246
|
private fun move(side: Side, open: Boolean, animate: Boolean) {
|
|
247
247
|
val drawer = drawer()
|
|
248
248
|
val silent = !drawer.callsBack
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (silent) record(side, open)
|
|
252
|
-
}
|
|
253
|
-
if (open && !animate && host.appeared) AppearanceDispatcher.withoutWillAppear(run) else run()
|
|
249
|
+
if (open) drawer.open(side, animate) else drawer.close(side, animate)
|
|
250
|
+
if (silent) record(side, open)
|
|
254
251
|
}
|
|
255
252
|
|
|
256
253
|
private fun record(side: Side, open: Boolean) {
|
|
@@ -18,15 +18,14 @@ import dev.apexnative.navigation.views.sidemenu.SideMenuView
|
|
|
18
18
|
* `testID`s. Each moment applies its part, from the side menu's resolved options
|
|
19
19
|
* (`SideMenuController.resolveShownOptions`: the current part's chain, the center's `sideMenu` over an open menu's) —
|
|
20
20
|
* at creation everything with the heights, at every appearance of a screen inside everything but the heights, at a
|
|
21
|
-
* merge the fields it names
|
|
21
|
+
* merge the fields it names (a width or a height at once, an open menu too), at a repaint the background and the
|
|
22
|
+
* `testID`s. The locks of `enabled` and the command
|
|
22
23
|
* `visible` are the node's ([SideMenuMotion]). Main thread only.
|
|
23
24
|
*/
|
|
24
25
|
internal class SideMenuPresenter(activity: Activity) : Presenter(activity, Options()) {
|
|
25
|
-
/** The side menu's [view] is made, with [options], resolved: the heights,
|
|
26
|
+
/** The side menu's [view] is made, with [options], resolved: the heights too, which only a merge changes after. */
|
|
26
27
|
fun applyOnCreation(view: SideMenuView, options: Options) {
|
|
27
|
-
|
|
28
|
-
view.setPartHeight(side, SideMenuRules.height(SideMenuRules.sideOf(options.sideMenu, side), ::toPixels))
|
|
29
|
-
}
|
|
28
|
+
applyHeights(view, options.sideMenu)
|
|
30
29
|
applyWidths(view, options.sideMenu)
|
|
31
30
|
applyBackground(view, options)
|
|
32
31
|
applyTestIds(view, options.sideMenu)
|
|
@@ -46,14 +45,18 @@ internal class SideMenuPresenter(activity: Activity) : Presenter(activity, Optio
|
|
|
46
45
|
/**
|
|
47
46
|
* A merge reached the side menu, its own or through a screen inside (`mergeOptions` step 3): the fields [options]
|
|
48
47
|
* name, with the values of the side menu's resolution now, so a screen off what shows changes nothing until it
|
|
49
|
-
* appears. A width
|
|
48
|
+
* appears. A width or a height resizes its menu at once, open or closed, as on iOS.
|
|
50
49
|
*/
|
|
51
50
|
override fun mergeOptions(viewController: ViewController<*>, options: Options) {
|
|
52
51
|
val layout = viewController.peekView() as? SideMenuView ?: return
|
|
53
52
|
val touchesBackground = SideMenuRules.touchesBackground(options)
|
|
54
53
|
val touchesTestIds = SideMenuRules.touchesTestIds(options.sideMenu)
|
|
55
|
-
|
|
54
|
+
val touchesWidths = SideMenuRules.touchesWidths(options.sideMenu)
|
|
55
|
+
val touchesHeights = SideMenuRules.touchesHeights(options.sideMenu)
|
|
56
|
+
if (!touchesBackground && !touchesTestIds && !touchesWidths && !touchesHeights) return
|
|
56
57
|
val shown = viewController.resolveShownOptions(DefaultOptions.current)
|
|
58
|
+
if (touchesWidths) applyWidths(layout, shown.sideMenu)
|
|
59
|
+
if (touchesHeights) applyHeights(layout, shown.sideMenu)
|
|
57
60
|
if (touchesBackground) applyBackground(layout, shown)
|
|
58
61
|
if (touchesTestIds) applyTestIds(layout, shown.sideMenu)
|
|
59
62
|
}
|
|
@@ -71,6 +74,12 @@ internal class SideMenuPresenter(activity: Activity) : Presenter(activity, Optio
|
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
77
|
+
private fun applyHeights(view: SideMenuView, options: SideMenuOptions) {
|
|
78
|
+
for (side in Side.entries) {
|
|
79
|
+
view.setPartHeight(side, SideMenuRules.height(SideMenuRules.sideOf(options, side), ::toPixels))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
74
83
|
// `layout.backgroundColor` on the drawer, its whole height, under the status bar; unset leaves the view as it is,
|
|
75
84
|
// `'NoColor'` clears it.
|
|
76
85
|
private fun applyBackground(view: View, options: Options) {
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt
CHANGED
|
@@ -54,6 +54,13 @@ internal object SideMenuRules {
|
|
|
54
54
|
fun touchesTestIds(merge: SideMenuOptions): Boolean =
|
|
55
55
|
merge.testID.hasValue() || merge.left.testID.hasValue() || merge.right.testID.hasValue()
|
|
56
56
|
|
|
57
|
+
/** A merge names `width` of a side: the menus take their widths again. */
|
|
58
|
+
fun touchesWidths(merge: SideMenuOptions): Boolean = merge.left.width.hasValue() || merge.right.width.hasValue()
|
|
59
|
+
|
|
60
|
+
/** A merge names `height` of a side: the menus take their heights again. */
|
|
61
|
+
fun touchesHeights(merge: SideMenuOptions): Boolean =
|
|
62
|
+
merge.left.height.hasValue() || merge.right.height.hasValue()
|
|
63
|
+
|
|
57
64
|
/** A merge names the background of the side menu (`layout.backgroundColor`). */
|
|
58
65
|
fun touchesBackground(merge: Options): Boolean = merge.layout.backgroundColor.hasValue()
|
|
59
66
|
|
|
@@ -64,7 +71,7 @@ internal object SideMenuRules {
|
|
|
64
71
|
fun wantedWidth(options: SideMenuSideOptions, toPixels: (Double) -> Int): Int? =
|
|
65
72
|
options.width.get(null)?.let { max(0, toPixels(it)) }
|
|
66
73
|
|
|
67
|
-
/** The height of a side, in pixels: `height` (dp) by [toPixels]
|
|
74
|
+
/** The height of a side, in pixels: `height` (dp) by [toPixels]; none: the whole height. */
|
|
68
75
|
fun height(options: SideMenuSideOptions, toPixels: (Double) -> Int): Int? =
|
|
69
76
|
options.height.get(null)?.let { max(0, toPixels(it)) }
|
|
70
77
|
|
|
@@ -31,6 +31,7 @@ import dev.apexnative.navigation.viewcontrollers.parent.ShownChildren
|
|
|
31
31
|
import dev.apexnative.navigation.viewcontrollers.stack.StackController
|
|
32
32
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
33
33
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
34
|
+
import kotlin.math.roundToInt
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* A split view: the primary pane beside the secondary one when the width takes both (a tablet, a phone in landscape).
|
|
@@ -489,7 +490,7 @@ class SplitViewController(
|
|
|
489
490
|
root.mergeOptions(options)
|
|
490
491
|
}
|
|
491
492
|
|
|
492
|
-
private fun dp(value: Double) = UiUtils.dpToPx(activity, value.toFloat()).
|
|
493
|
+
private fun dp(value: Double) = UiUtils.dpToPx(activity, value.toFloat()).roundToInt()
|
|
493
494
|
|
|
494
495
|
companion object {
|
|
495
496
|
private const val PRIMARY = "primary"
|
|
@@ -60,6 +60,7 @@ import dev.apexnative.navigation.views.stack.topbar.TopBarDefaults
|
|
|
60
60
|
import dev.apexnative.navigation.views.stack.topbar.titlebar.BarButtonComponentCell
|
|
61
61
|
import dev.apexnative.navigation.views.stack.topbar.titlebar.ButtonBar
|
|
62
62
|
import dev.apexnative.navigation.views.stack.topbar.titlebar.IconBackgroundDrawable
|
|
63
|
+
import kotlin.math.roundToInt
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* What the bar of one stack shows: the options of the screen on top of the stack, resolved (a pair's options screen),
|
|
@@ -881,7 +882,7 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
881
882
|
else -> TitleBar.Alignment.Start
|
|
882
883
|
}
|
|
883
884
|
|
|
884
|
-
private fun dp(value: Double): Int = UiUtils.dpToPx(activity, value.toFloat()).
|
|
885
|
+
private fun dp(value: Double): Int = UiUtils.dpToPx(activity, value.toFloat()).roundToInt()
|
|
885
886
|
|
|
886
887
|
// What makes a button's look, as a text: the parts that differ make it apply again.
|
|
887
888
|
private fun buttonKey(context: Context, button: ButtonOptions): String = listOf(
|
|
@@ -98,8 +98,8 @@ class BottomTabs(context: Context) : BottomNavigationView(MaterialTheme.context(
|
|
|
98
98
|
var iconInactiveColor: Int? = null
|
|
99
99
|
var titleActiveColor: Int? = null
|
|
100
100
|
var titleInactiveColor: Int? = null
|
|
101
|
-
var iconWidth:
|
|
102
|
-
var iconHeight:
|
|
101
|
+
var iconWidth: Double? = null
|
|
102
|
+
var iconHeight: Double? = null
|
|
103
103
|
var typeface: Typeface? = null
|
|
104
104
|
var activeTextSize: Float? = null
|
|
105
105
|
var inactiveTextSize: Float? = null
|
|
@@ -246,9 +246,9 @@ class BottomTabs(context: Context) : BottomNavigationView(MaterialTheme.context(
|
|
|
246
246
|
|
|
247
247
|
fun setTabTestId(index: Int, testId: String?) = updateStyle(index) { it.testId = testId }
|
|
248
248
|
|
|
249
|
-
fun setIconWidth(index: Int, width:
|
|
249
|
+
fun setIconWidth(index: Int, width: Double?) = updateStyle(index) { it.iconWidth = width }
|
|
250
250
|
|
|
251
|
-
fun setIconHeight(index: Int, height:
|
|
251
|
+
fun setIconHeight(index: Int, height: Double?) = updateStyle(index) { it.iconHeight = height }
|
|
252
252
|
|
|
253
253
|
fun setTitleTypeface(index: Int, typeface: Typeface?) = updateStyle(index) { it.typeface = typeface }
|
|
254
254
|
|
|
@@ -475,7 +475,7 @@ class BottomTabs(context: Context) : BottomNavigationView(MaterialTheme.context(
|
|
|
475
475
|
view.setIconTintList(tint(style.iconActiveColor, style.iconInactiveColor, itemIconTintList))
|
|
476
476
|
view.setTextColor(tint(style.titleActiveColor, style.titleInactiveColor, itemTextColor))
|
|
477
477
|
val iconSize = style.iconWidth ?: style.iconHeight
|
|
478
|
-
view.setIconSize(if (iconSize != null) UiUtils.
|
|
478
|
+
view.setIconSize(if (iconSize != null) UiUtils.dpToPixels(context, iconSize) else itemIconSize)
|
|
479
479
|
view.findViewById<TextView>(MaterialR.id.navigation_bar_item_small_label_view)?.let {
|
|
480
480
|
style.typeface?.let { typeface -> it.typeface = typeface }
|
|
481
481
|
style.inactiveTextSize?.let { size -> it.setTextSize(TypedValue.COMPLEX_UNIT_SP, size) }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package dev.apexnative.navigation.views.element
|
|
2
|
+
|
|
3
|
+
import dev.apexnative.navigation.utils.DevelopmentLog
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The isolation of every element of the transitions: [work], a step of the element named [element] (its `nativeID` in
|
|
7
|
+
* quotes, or a pair's two), runs; an exception in it skips that element only — one line in the log
|
|
8
|
+
* ([elementSkipped]), and false — while the rest of the transition plays and its command completes.
|
|
9
|
+
*/
|
|
10
|
+
internal inline fun elementStep(
|
|
11
|
+
element: String,
|
|
12
|
+
noinline log: (String) -> Unit = DevelopmentLog::warn,
|
|
13
|
+
work: () -> Unit,
|
|
14
|
+
): Boolean = try {
|
|
15
|
+
work()
|
|
16
|
+
true
|
|
17
|
+
} catch (e: Exception) {
|
|
18
|
+
elementSkipped(element, e.toString(), log)
|
|
19
|
+
false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The line in the log for the element named [element], skipped for [reason]: it shows and hides with its screen. */
|
|
23
|
+
internal fun elementSkipped(element: String, reason: String, log: (String) -> Unit = DevelopmentLog::warn) =
|
|
24
|
+
log("[ApexNavigation] the transition's element $element is skipped, it shows and hides with its screen while the " +
|
|
25
|
+
"rest plays: $reason")
|
|
26
|
+
|
|
27
|
+
/** The name of an element in the log. */
|
|
28
|
+
internal fun elementName(nativeId: String) = "'$nativeId'"
|
|
29
|
+
|
|
30
|
+
/** The name of a shared pair in the log. */
|
|
31
|
+
internal fun pairName(fromId: String, toId: String) = "'$fromId' -> '$toId'"
|
|
@@ -7,7 +7,6 @@ import android.os.Build
|
|
|
7
7
|
import android.util.Log
|
|
8
8
|
import android.view.View
|
|
9
9
|
import android.view.ViewGroup
|
|
10
|
-
import com.facebook.react.uimanager.util.ReactFindViewUtil
|
|
11
10
|
import com.facebook.react.views.view.ReactViewGroup
|
|
12
11
|
import dev.apexnative.navigation.options.ElementTransitionOptions
|
|
13
12
|
import dev.apexnative.navigation.options.LayoutAnimation
|
|
@@ -23,12 +22,13 @@ import dev.apexnative.navigation.views.element.animators.SharedAnimatorFactory
|
|
|
23
22
|
* The elements of one transition: the element transitions and the targets of the shared elements found by their
|
|
24
23
|
* `nativeID` (depth first, the first match) in the whole view of the screen each side shows — an element in the screen
|
|
25
24
|
* that goes first, then in the one that comes; a pair's `fromId` in the screen that goes, its `toId` in the one that
|
|
26
|
-
* comes
|
|
27
|
-
*
|
|
28
|
-
* it
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
25
|
+
* comes; each screen is walked once for all of them ([TransitionViews]). What isn't found, what is in another
|
|
26
|
+
* transition's layer and an image that isn't loaded is skipped; so is an element whose start fails ([elementStep]):
|
|
27
|
+
* it goes back at once and the rest plays. Each view found moves into a [TransitionLayer] at its place on the screen,
|
|
28
|
+
* in the order its hierarchy draws it, and an [ElementStub] takes its index in its parent. A pair's `from` stays where
|
|
29
|
+
* it is, hidden while its copy flies. [finish] puts every view back at its index with its layout, its pivot and the
|
|
30
|
+
* values of its properties read before — unless React removed its stub meanwhile: then it is gone for good —, shows
|
|
31
|
+
* the `from`s again and takes the layer away. Main thread only.
|
|
32
32
|
*/
|
|
33
33
|
internal class ElementTransition private constructor(private val layer: TransitionLayer) {
|
|
34
34
|
// A view in the layer: where it came from and what it was.
|
|
@@ -89,18 +89,41 @@ internal class ElementTransition private constructor(private val layer: Transiti
|
|
|
89
89
|
view.rotationX, view.rotationY, view.pivotX, view.pivotY,
|
|
90
90
|
)
|
|
91
91
|
val stub = ElementStub(view.context, view)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
try {
|
|
93
|
+
parent.removeViewAt(index)
|
|
94
|
+
parent.addView(stub, index, params)
|
|
95
|
+
stub.layout(frame.left, frame.top, frame.right, frame.bottom)
|
|
96
|
+
val left = originX + frame.left
|
|
97
|
+
val top = originY + frame.top
|
|
98
|
+
layer.put(view, left, top, frame.width(), frame.height())
|
|
99
|
+
} catch (e: RuntimeException) {
|
|
100
|
+
putBack(view, stub, parent, index, params, frame)
|
|
101
|
+
throw e
|
|
102
|
+
}
|
|
98
103
|
// Scale and rotations turn about the center.
|
|
99
104
|
view.pivotX = frame.width() / 2f
|
|
100
105
|
view.pivotY = frame.height() / 2f
|
|
101
106
|
return Moved(view, stub, params, frame, values, pivotSet, originX, originY).also { moved += it }
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
// A move that failed half way undone: the view back at its index in its parent, the stub gone.
|
|
110
|
+
private fun putBack(
|
|
111
|
+
view: View,
|
|
112
|
+
stub: ElementStub,
|
|
113
|
+
parent: ViewGroup,
|
|
114
|
+
index: Int,
|
|
115
|
+
params: ViewGroup.LayoutParams,
|
|
116
|
+
frame: Rect,
|
|
117
|
+
) {
|
|
118
|
+
if (view.parent === layer) layer.removeView(view)
|
|
119
|
+
val stubIndex = parent.indexOfChild(stub)
|
|
120
|
+
if (stubIndex >= 0) parent.removeViewAt(stubIndex)
|
|
121
|
+
if (view.parent == null) {
|
|
122
|
+
parent.addView(view, index.coerceAtMost(parent.childCount), params)
|
|
123
|
+
view.layout(frame.left, frame.top, frame.right, frame.bottom)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
104
127
|
private fun giveBack(item: Moved) {
|
|
105
128
|
val view = item.view
|
|
106
129
|
// A commit of Fabric meanwhile laid it out in its parent's coordinates: the newest such frame holds.
|
|
@@ -156,13 +179,26 @@ internal class ElementTransition private constructor(private val layer: Transiti
|
|
|
156
179
|
from, to, toParent, layer, options, fallbackDuration, DevelopmentLog::warn,
|
|
157
180
|
) ?: return false
|
|
158
181
|
val alpha = item.values[ALPHA]
|
|
182
|
+
val fromAlpha = from.alpha
|
|
159
183
|
to.alpha = 0f
|
|
160
|
-
hidden += from to
|
|
184
|
+
hidden += from to fromAlpha
|
|
161
185
|
flight.animator.addListener(object : AnimatorListenerAdapter() {
|
|
186
|
+
private var stopped = false
|
|
187
|
+
|
|
162
188
|
override fun onAnimationStart(animation: Animator) {
|
|
189
|
+
if (stopped) return
|
|
163
190
|
from.alpha = 0f
|
|
164
191
|
if (!flight.drawsPictures) to.alpha = alpha
|
|
165
192
|
}
|
|
193
|
+
|
|
194
|
+
// A flight whose start or frame failed stops (`MoveAnimator`), maybe inside its own start: `from` shows
|
|
195
|
+
// again in its screen, and a `to` that moves itself in its place, as at any stop (a flight of pictures
|
|
196
|
+
// shows its `to` at its end).
|
|
197
|
+
override fun onAnimationCancel(animation: Animator) {
|
|
198
|
+
stopped = true
|
|
199
|
+
from.alpha = fromAlpha
|
|
200
|
+
if (!flight.drawsPictures) to.alpha = alpha
|
|
201
|
+
}
|
|
166
202
|
})
|
|
167
203
|
animators += flight.animator
|
|
168
204
|
return true
|
|
@@ -195,20 +231,23 @@ internal class ElementTransition private constructor(private val layer: Transiti
|
|
|
195
231
|
): ElementTransition? {
|
|
196
232
|
val fromView = from?.let { AppearanceDispatcher.visibleNode(it).peekView() }
|
|
197
233
|
val toView = AppearanceDispatcher.visibleNode(to).peekView()
|
|
234
|
+
val transitions = animation.elementTransitions.transitions
|
|
235
|
+
val shared = animation.sharedElements.get()
|
|
236
|
+
val views = TransitionViews(shared, transitions, fromView, toView)
|
|
198
237
|
val taken = HashSet<View>()
|
|
199
238
|
val elements = ArrayList<Pair<ElementTransitionOptions, View>>()
|
|
200
|
-
for (element in
|
|
239
|
+
for (element in transitions) {
|
|
201
240
|
if (element.id.isEmpty()) continue
|
|
202
|
-
val view =
|
|
241
|
+
val view = found(views.leaving(element.id)) ?: found(views.arriving(element.id)) ?: continue
|
|
203
242
|
if (TransitionLayer.holds(view) || !taken.add(view)) continue
|
|
204
243
|
elements += element to view
|
|
205
244
|
}
|
|
206
245
|
val pairs = ArrayList<Triple<SharedElementTransitionOptions, View, View>>()
|
|
207
|
-
for (pair in
|
|
246
|
+
for (pair in shared) {
|
|
208
247
|
val fromId = pair.fromId.get(null) ?: continue
|
|
209
248
|
val toId = pair.toId.get(null) ?: continue
|
|
210
|
-
val source =
|
|
211
|
-
val target =
|
|
249
|
+
val source = found(views.leaving(fromId)) ?: continue
|
|
250
|
+
val target = found(views.arriving(toId)) ?: continue
|
|
212
251
|
if (source === target || TransitionLayer.holds(source) || TransitionLayer.holds(target)) continue
|
|
213
252
|
if (target in taken || source in taken) continue
|
|
214
253
|
if (!loaded(source, fromId) || !loaded(target, toId)) continue
|
|
@@ -246,8 +285,7 @@ internal class ElementTransition private constructor(private val layer: Transiti
|
|
|
246
285
|
null
|
|
247
286
|
}
|
|
248
287
|
|
|
249
|
-
private fun
|
|
250
|
-
root?.let { ReactFindViewUtil.findView(it, nativeId) }?.takeUnless { it is ElementStub }
|
|
288
|
+
private fun found(view: View?): View? = view?.takeUnless { it is ElementStub }
|
|
251
289
|
|
|
252
290
|
// An image not loaded drops out of the transition; its wait logged it already, else it is logged now.
|
|
253
291
|
private fun loaded(view: View, id: String): Boolean {
|
|
@@ -258,24 +296,44 @@ internal class ElementTransition private constructor(private val layer: Transiti
|
|
|
258
296
|
}
|
|
259
297
|
|
|
260
298
|
// The views into the layer in the order they are drawn, then their animators; a pair that doesn't fly goes back at
|
|
261
|
-
// once.
|
|
299
|
+
// once. Each element's steps run on their own ([elementStep]): one that fails goes back, the others play.
|
|
262
300
|
private fun fill(
|
|
263
301
|
host: ViewGroup,
|
|
264
302
|
elements: List<Pair<ElementTransitionOptions, View>>,
|
|
265
303
|
pairs: List<Triple<SharedElementTransitionOptions, View, View>>,
|
|
266
304
|
fallbackDuration: Long,
|
|
267
305
|
) {
|
|
306
|
+
val names = HashMap<View, String>()
|
|
307
|
+
for ((options, view) in elements) names[view] = elementName(options.id)
|
|
308
|
+
for ((options, _, target) in pairs) names[target] = pairName(options.fromId.get(""), options.toId.get(""))
|
|
268
309
|
val views = elements.map { it.second } + pairs.map { it.third }
|
|
269
310
|
val parents = views.associateWith { it.parent as? View }
|
|
270
311
|
val placed = HashMap<View, Moved>()
|
|
271
|
-
for (view in DrawOrder.sorted(views, host) { it })
|
|
272
|
-
|
|
312
|
+
for (view in DrawOrder.sorted(views, host) { it }) {
|
|
313
|
+
var item: Moved? = null
|
|
314
|
+
if (elementStep(names.getValue(view)) { item = moveIntoLayer(view) }) item?.let { placed[view] = it }
|
|
315
|
+
}
|
|
316
|
+
for ((options, view) in elements) {
|
|
317
|
+
val item = placed[view] ?: continue
|
|
318
|
+
if (!elementStep(names.getValue(view)) { addElement(options, item) }) goBack(item)
|
|
319
|
+
}
|
|
273
320
|
for ((options, source, target) in pairs) {
|
|
274
321
|
val item = placed[target] ?: continue
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
322
|
+
var flies = false
|
|
323
|
+
elementStep(names.getValue(target)) {
|
|
324
|
+
flies = addPair(options, source, item, parents[target], fallbackDuration)
|
|
278
325
|
}
|
|
326
|
+
if (!flies) goBack(item)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// An element that doesn't play leaves the layer now; one that can't go back is logged, the others go on.
|
|
331
|
+
private fun goBack(item: Moved) {
|
|
332
|
+
moved -= item
|
|
333
|
+
try {
|
|
334
|
+
giveBack(item)
|
|
335
|
+
} catch (e: RuntimeException) {
|
|
336
|
+
Log.w(NavigationLog.TAG, "[ApexNavigation] An element of a transition couldn't go back", e)
|
|
279
337
|
}
|
|
280
338
|
}
|
|
281
339
|
}
|
|
@@ -3,7 +3,6 @@ package dev.apexnative.navigation.views.element
|
|
|
3
3
|
import android.util.Log
|
|
4
4
|
import android.view.Choreographer
|
|
5
5
|
import android.view.View
|
|
6
|
-
import com.facebook.react.uimanager.util.ReactFindViewUtil
|
|
7
6
|
import dev.apexnative.navigation.options.LayoutAnimation
|
|
8
7
|
import dev.apexnative.navigation.react.events.AppearanceDispatcher
|
|
9
8
|
import dev.apexnative.navigation.utils.DevelopmentLog
|
|
@@ -72,12 +71,14 @@ internal object TargetsWait {
|
|
|
72
71
|
): () -> Unit {
|
|
73
72
|
val fromView = from?.let { AppearanceDispatcher.visibleNode(it).peekView() }
|
|
74
73
|
val toView = AppearanceDispatcher.visibleNode(to).peekView()
|
|
74
|
+
val pairs = animation.sharedElements.get()
|
|
75
|
+
val views = TransitionViews(pairs, emptyList(), fromView, toView)
|
|
75
76
|
val images = ArrayList<ImageWait.Image?>()
|
|
76
|
-
for (pair in
|
|
77
|
+
for (pair in pairs) {
|
|
77
78
|
val fromId = pair.fromId.get(null) ?: continue
|
|
78
79
|
val toId = pair.toId.get(null) ?: continue
|
|
79
|
-
val source =
|
|
80
|
-
val target =
|
|
80
|
+
val source = views.leaving(fromId) ?: continue
|
|
81
|
+
val target = views.arriving(toId) ?: continue
|
|
81
82
|
images += imageOf(source, fromId)
|
|
82
83
|
images += imageOf(target, toId)
|
|
83
84
|
}
|