@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
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
package dev.apexnative.navigation.views.element.animators
|
|
2
2
|
|
|
3
3
|
import android.animation.Animator
|
|
4
|
-
import androidx.core.animation.doOnEnd
|
|
5
|
-
import androidx.core.animation.doOnStart
|
|
6
4
|
import com.facebook.drawee.drawable.ScalingUtils
|
|
7
5
|
import com.facebook.react.views.image.ReactImageView
|
|
8
6
|
import dev.apexnative.navigation.views.animations.ValueInterpolation
|
|
@@ -45,14 +43,14 @@ internal class ReactImageAnimator(
|
|
|
45
43
|
to.rotation = ValueInterpolation.number(fromRotation, toRotation, f, time)
|
|
46
44
|
to.invalidate()
|
|
47
45
|
}
|
|
48
|
-
|
|
46
|
+
onStart {
|
|
49
47
|
savedFade = to.hierarchy.fadeDuration
|
|
50
48
|
to.hierarchy.fadeDuration = 0
|
|
51
49
|
to.hierarchy.setActualImageScaleType(scale)
|
|
52
50
|
frame(0f)
|
|
53
51
|
}
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
onFrame { frame(it) }
|
|
53
|
+
onEnd {
|
|
56
54
|
to.hierarchy.setActualImageScaleType(toType)
|
|
57
55
|
to.hierarchy.fadeDuration = savedFade
|
|
58
56
|
to.translationX = 0f
|
|
@@ -10,6 +10,7 @@ import dev.apexnative.navigation.options.SharedElementTransitionOptions
|
|
|
10
10
|
import dev.apexnative.navigation.views.animations.AnimationRules
|
|
11
11
|
import dev.apexnative.navigation.views.animations.Curves
|
|
12
12
|
import dev.apexnative.navigation.views.element.TransitionLayer
|
|
13
|
+
import dev.apexnative.navigation.views.element.pairName
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* The animator of a shared element by the kinds of its two views, chosen by explicit classes, never by reflection:
|
|
@@ -55,6 +56,7 @@ internal object SharedAnimatorFactory {
|
|
|
55
56
|
else -> moves(PlainViewAnimator(from, to, layer).create())
|
|
56
57
|
} ?: return null
|
|
57
58
|
val animator = flight.animator
|
|
59
|
+
(animator as? MoveAnimator)?.element = pairName(options.fromId.get(""), options.toId.get(""))
|
|
58
60
|
val duration = AnimationRules.durationOf(options.duration.get(null), fallbackDuration)
|
|
59
61
|
animator.duration = duration
|
|
60
62
|
animator.startDelay = AnimationRules.delayOf(options.startDelay.get(null))
|
|
@@ -11,8 +11,6 @@ import android.text.Layout
|
|
|
11
11
|
import android.view.View
|
|
12
12
|
import android.view.ViewGroup
|
|
13
13
|
import android.widget.TextView
|
|
14
|
-
import androidx.core.animation.doOnEnd
|
|
15
|
-
import androidx.core.animation.doOnStart
|
|
16
14
|
import com.facebook.react.views.text.ReactTextView
|
|
17
15
|
import dev.apexnative.navigation.options.SharedElementTransitionOptions
|
|
18
16
|
import dev.apexnative.navigation.utils.TextViewUtils
|
|
@@ -42,16 +40,16 @@ class TextChangeAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactTe
|
|
|
42
40
|
val drawable = TextMorphDrawable(start, end)
|
|
43
41
|
// Everything changes between the start and the end of the animation: a cancelled one ends too.
|
|
44
42
|
return MoveAnimator().apply {
|
|
45
|
-
|
|
43
|
+
onStart {
|
|
46
44
|
drawable.setBounds(0, 0, layer.width, layer.height)
|
|
47
45
|
layer.overlay.add(drawable)
|
|
48
46
|
to.alpha = 0f
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
drawable.update(it
|
|
48
|
+
onFrame {
|
|
49
|
+
drawable.update(it, curve.getInterpolation(it))
|
|
52
50
|
layer.invalidate()
|
|
53
51
|
}
|
|
54
|
-
|
|
52
|
+
onEnd {
|
|
55
53
|
layer.overlay.remove(drawable)
|
|
56
54
|
layer.invalidate()
|
|
57
55
|
to.alpha = end.alpha
|
|
@@ -12,8 +12,6 @@ import android.graphics.RectF
|
|
|
12
12
|
import android.graphics.drawable.Drawable
|
|
13
13
|
import android.view.View
|
|
14
14
|
import android.view.ViewGroup
|
|
15
|
-
import androidx.core.animation.doOnEnd
|
|
16
|
-
import androidx.core.animation.doOnStart
|
|
17
15
|
import com.facebook.react.views.view.ReactViewGroup
|
|
18
16
|
import dev.apexnative.navigation.options.SharedElementTransitionOptions
|
|
19
17
|
import dev.apexnative.navigation.utils.ViewUtils
|
|
@@ -38,16 +36,16 @@ class ViewChangeAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactVi
|
|
|
38
36
|
val drawable = ViewMorphDrawable(start, end)
|
|
39
37
|
// Everything changes between the start and the end of the animation: a cancelled one ends too.
|
|
40
38
|
return MoveAnimator().apply {
|
|
41
|
-
|
|
39
|
+
onStart {
|
|
42
40
|
drawable.setBounds(0, 0, layer.width, layer.height)
|
|
43
41
|
layer.overlay.add(drawable)
|
|
44
42
|
to.alpha = 0f
|
|
45
43
|
}
|
|
46
|
-
|
|
47
|
-
drawable.progress = curve.getInterpolation(it
|
|
44
|
+
onFrame {
|
|
45
|
+
drawable.progress = curve.getInterpolation(it)
|
|
48
46
|
layer.invalidate()
|
|
49
47
|
}
|
|
50
|
-
|
|
48
|
+
onEnd {
|
|
51
49
|
layer.overlay.remove(drawable)
|
|
52
50
|
layer.invalidate()
|
|
53
51
|
to.alpha = end.alpha
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package dev.apexnative.navigation.views.stack
|
|
2
|
+
|
|
3
|
+
import android.graphics.drawable.Drawable
|
|
4
|
+
import android.view.View
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What fills the room beside a moving screen in a transition (`StackLayout.extendEdge`), where the system's
|
|
8
|
+
* transitions between activities stretch a window's edge (`<extend>` of `activity_open_*` and `activity_close_*`): the
|
|
9
|
+
* screen's own background, which is what its edge shows unless its content covers it. Taken without drawing the
|
|
10
|
+
* screen, at the start of every push and pop. Main thread only.
|
|
11
|
+
*/
|
|
12
|
+
internal object ScreenEdge {
|
|
13
|
+
/** A copy of [view]'s background, stretched across the room; none without one. */
|
|
14
|
+
fun pictureOf(view: View): Drawable? = view.background?.constantState?.newDrawable(view.resources)?.mutate()
|
|
15
|
+
}
|
|
@@ -2,6 +2,8 @@ package dev.apexnative.navigation.views.stack
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.graphics.Canvas
|
|
6
|
+
import android.graphics.drawable.Drawable
|
|
5
7
|
import android.view.Gravity
|
|
6
8
|
import android.view.View
|
|
7
9
|
import android.view.ViewGroup
|
|
@@ -18,7 +20,8 @@ import java.util.WeakHashMap
|
|
|
18
20
|
* moves between its places without leaving it, so it doesn't leave and join the window again; a covered screen that
|
|
19
21
|
* comes back gets the last window insets the stack got before it joins, and nothing asks the window to send them again
|
|
20
22
|
* (CoordinatorLayout asks for insets only as it joins the window itself and only with `fitsSystemWindows`, which this
|
|
21
|
-
* view doesn't set).
|
|
23
|
+
* view doesn't set). In a transition of the stack's own the bar is drawn with the screen it goes with
|
|
24
|
+
* ([barTravels]). Main thread only.
|
|
22
25
|
*/
|
|
23
26
|
@SuppressLint("ViewConstructor")
|
|
24
27
|
class StackLayout(context: Context) : CoordinatorLayout(context) {
|
|
@@ -134,4 +137,137 @@ class StackLayout(context: Context) : CoordinatorLayout(context) {
|
|
|
134
137
|
requestLayout()
|
|
135
138
|
invalidate()
|
|
136
139
|
}
|
|
140
|
+
|
|
141
|
+
// The edges of the screens in a transition: drawn beside each, under it, where it moves and as transparent as it.
|
|
142
|
+
private val edges = HashMap<View, Edge>()
|
|
143
|
+
|
|
144
|
+
private class Edge(val picture: Drawable, val width: Int, val atStart: Boolean)
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* [view] extends its start edge ([atStart]) or its end edge by [width] pixels while it moves, as the system's
|
|
148
|
+
* transitions between activities extend a window: its background fills the room it moves away from, so the screen
|
|
149
|
+
* under it never shows at its edge. [dropEdge] ends it. Each frame of the move calls [edgesMoved].
|
|
150
|
+
*/
|
|
151
|
+
fun extendEdge(view: View, atStart: Boolean, width: Int) {
|
|
152
|
+
val left = atStart != (layoutDirection == LAYOUT_DIRECTION_RTL)
|
|
153
|
+
val picture = ScreenEdge.pictureOf(view) ?: return
|
|
154
|
+
edges[view] = Edge(picture, width, left)
|
|
155
|
+
invalidate()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** [view] moves no more: its edge goes. */
|
|
159
|
+
fun dropEdge(view: View) {
|
|
160
|
+
if (edges.remove(view) != null) invalidate()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** A screen with an extended edge moved: the edges are drawn again where the screens are now. */
|
|
164
|
+
fun edgesMoved() {
|
|
165
|
+
if (edges.isNotEmpty()) invalidate()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// The bar's travel in a transition of the stack's own: the screen the bar goes with, the screen that leaves with the
|
|
169
|
+
// picture of the bar as it was, and whether that one lies over the bar (a pop).
|
|
170
|
+
private var barScreen: View? = null
|
|
171
|
+
private var barLeaving: View? = null
|
|
172
|
+
private var barPicture: Drawable? = null
|
|
173
|
+
private var leavingOver = false
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The bar goes with [screen] in a transition of the stack's own: it is drawn as [screen] moves (its translation,
|
|
177
|
+
* scale and rotation around its pivot, its alpha). [picture], laid where the bar is, goes the same way with
|
|
178
|
+
* [leaving]: it is drawn right over that screen, outside its bounds too (a screen laid out under the bar). With
|
|
179
|
+
* [over] the leaving screen and its picture lie over the bar (a pop), else under it. Only the drawing moves: the
|
|
180
|
+
* bar's views and touches stay in place. [endBarTravel] ends it.
|
|
181
|
+
*/
|
|
182
|
+
fun barTravels(screen: View, leaving: View?, picture: Drawable?, over: Boolean) {
|
|
183
|
+
barScreen = screen
|
|
184
|
+
barLeaving = leaving
|
|
185
|
+
barPicture = picture
|
|
186
|
+
leavingOver = over && leaving != null
|
|
187
|
+
invalidate()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** The bar is drawn in its place again, over the screens; the picture goes. */
|
|
191
|
+
fun endBarTravel() {
|
|
192
|
+
if (barScreen == null) return
|
|
193
|
+
barScreen = null
|
|
194
|
+
barLeaving = null
|
|
195
|
+
barPicture = null
|
|
196
|
+
leavingOver = false
|
|
197
|
+
invalidate()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
override fun dispatchDraw(canvas: Canvas) {
|
|
201
|
+
super.dispatchDraw(canvas)
|
|
202
|
+
// The screens' animators change only their own properties, which don't draw this view again: each frame of
|
|
203
|
+
// the bar's travel asks for the next.
|
|
204
|
+
if (barScreen != null) postInvalidateOnAnimation()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
|
|
208
|
+
edges[child]?.let { drawEdge(canvas, child, it) }
|
|
209
|
+
val screen = barScreen ?: return super.drawChild(canvas, child, drawingTime)
|
|
210
|
+
// The leaving screen, while it is still here, and its picture; on a pop the bar right under them.
|
|
211
|
+
val leaving = barLeaving?.takeIf(::holds)
|
|
212
|
+
val under = leaving.takeIf { leavingOver }
|
|
213
|
+
if (child === topBar) {
|
|
214
|
+
if (under != null) return false
|
|
215
|
+
return drawWith(canvas, screen, topBar.left.toFloat(), topBar.top + topBar.translationY,
|
|
216
|
+
topBar.right.toFloat(), topBar.bottom + topBar.translationY) {
|
|
217
|
+
super.drawChild(canvas, topBar, drawingTime)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (child === under && topBar.visibility == VISIBLE) {
|
|
221
|
+
drawWith(canvas, screen, topBar.left.toFloat(), topBar.top + topBar.translationY,
|
|
222
|
+
topBar.right.toFloat(), topBar.bottom + topBar.translationY) {
|
|
223
|
+
super.drawChild(canvas, topBar, drawingTime)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
val drawn = super.drawChild(canvas, child, drawingTime)
|
|
227
|
+
val picture = barPicture
|
|
228
|
+
if (child === leaving && picture != null) {
|
|
229
|
+
val bounds = picture.bounds
|
|
230
|
+
drawWith(canvas, child, bounds.left.toFloat(), bounds.top.toFloat(), bounds.right.toFloat(),
|
|
231
|
+
bounds.bottom.toFloat()) {
|
|
232
|
+
picture.draw(canvas)
|
|
233
|
+
false
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return drawn
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Draws with [screen]'s transform and alpha what [draw] draws within the bounds given, in this view's coordinates.
|
|
240
|
+
private fun drawWith(
|
|
241
|
+
canvas: Canvas,
|
|
242
|
+
screen: View,
|
|
243
|
+
left: Float,
|
|
244
|
+
top: Float,
|
|
245
|
+
right: Float,
|
|
246
|
+
bottom: Float,
|
|
247
|
+
draw: () -> Boolean,
|
|
248
|
+
): Boolean {
|
|
249
|
+
val alpha = if (screen.visibility == VISIBLE) screen.alpha else 0f
|
|
250
|
+
if (alpha <= 0f) return false
|
|
251
|
+
val saved = canvas.save()
|
|
252
|
+
canvas.translate(screen.left.toFloat(), screen.top.toFloat())
|
|
253
|
+
canvas.concat(screen.matrix)
|
|
254
|
+
canvas.translate(-screen.left.toFloat(), -screen.top.toFloat())
|
|
255
|
+
if (alpha < 1f) canvas.saveLayerAlpha(left, top, right, bottom, (alpha * 255).toInt())
|
|
256
|
+
val drawn = draw()
|
|
257
|
+
canvas.restoreToCount(saved)
|
|
258
|
+
return drawn
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// At the child's place to the fraction of a pixel, as its own translation draws it, and one pixel under it: no seam
|
|
262
|
+
// of the screen below shows between them.
|
|
263
|
+
private fun drawEdge(canvas: Canvas, child: View, edge: Edge) {
|
|
264
|
+
if (child.visibility != VISIBLE || child.alpha <= 0f) return
|
|
265
|
+
val x = if (edge.atStart) child.left + child.translationX - edge.width else child.right + child.translationX - 1f
|
|
266
|
+
val saved = canvas.save()
|
|
267
|
+
canvas.translate(x, child.top + child.translationY)
|
|
268
|
+
edge.picture.setBounds(0, 0, edge.width + 1, child.height)
|
|
269
|
+
edge.picture.alpha = (child.alpha * 255).toInt()
|
|
270
|
+
edge.picture.draw(canvas)
|
|
271
|
+
canvas.restoreToCount(saved)
|
|
272
|
+
}
|
|
137
273
|
}
|
|
@@ -118,6 +118,8 @@ class TitleBar(context: Context) : ViewGroup(context) {
|
|
|
118
118
|
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
119
119
|
val inner = max(width - sideInsetsBox[0] - sideInsetsBox[1], 0)
|
|
120
120
|
val exactHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
121
|
+
alignButtons(leftButtonBar, height)
|
|
122
|
+
alignButtons(rightButtonBar, height)
|
|
121
123
|
leftButtonBar.measure(MeasureSpec.makeMeasureSpec(inner, MeasureSpec.AT_MOST), exactHeight)
|
|
122
124
|
val rightRoom = max(inner - leftButtonBar.measuredWidth, 0)
|
|
123
125
|
rightButtonBar.measure(MeasureSpec.makeMeasureSpec(rightRoom, MeasureSpec.AT_MOST), exactHeight)
|
|
@@ -192,6 +194,12 @@ class TitleBar(context: Context) : ViewGroup(context) {
|
|
|
192
194
|
return min(max(middle, startBar), max(inner - endBar - width, startBar))
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
// A toolbar stands its back button and its menu at the top of its minimum height, centered in it (`buttonGravity`
|
|
198
|
+
// is top): with the row's height as that minimum, they stand in the middle of the row, as the title does.
|
|
199
|
+
private fun alignButtons(toolbar: MaterialToolbar, height: Int) {
|
|
200
|
+
if (toolbar.minimumHeight != height) toolbar.minimumHeight = height
|
|
201
|
+
}
|
|
202
|
+
|
|
195
203
|
// A toolbar that holds only buttons: no insets of its own around them (the row places them).
|
|
196
204
|
private fun plain(toolbar: MaterialToolbar) {
|
|
197
205
|
// Before the toolbar makes its menu, which takes the popup theme once: "⋮" in the current mode.
|
|
@@ -200,7 +208,6 @@ class TitleBar(context: Context) : ViewGroup(context) {
|
|
|
200
208
|
toolbar.contentInsetStartWithNavigation = 0
|
|
201
209
|
toolbar.contentInsetEndWithActions = 0
|
|
202
210
|
toolbar.setPadding(0, 0, 0, 0)
|
|
203
|
-
toolbar.minimumHeight = 0
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
private companion object {
|
|
@@ -55,6 +55,8 @@ class TopBar(context: Context) : FrameLayout(themed(context)) {
|
|
|
55
55
|
private var heightChanged = false
|
|
56
56
|
// How far the large title is collapsed, in pixels.
|
|
57
57
|
private var largeTitleShift = 0
|
|
58
|
+
// `topBar.elevation`, in pixels: the bar's elevation unless it is flat.
|
|
59
|
+
private var shadow = 0f
|
|
58
60
|
|
|
59
61
|
/** The toolbar of the right buttons: its menu holds them, the overflow items and the search's magnifier. */
|
|
60
62
|
val rightButtonBar: MaterialToolbar
|
|
@@ -187,14 +189,27 @@ class TopBar(context: Context) : FrameLayout(themed(context)) {
|
|
|
187
189
|
* The shadow of `topBar.elevation`, in pixels: its colors are black (Android 9 and later read them), none at 0.
|
|
188
190
|
*/
|
|
189
191
|
fun setShadow(elevation: Float) {
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
+
if (shadow == elevation) return
|
|
193
|
+
shadow = elevation
|
|
194
|
+
if (!isFlat) this.elevation = elevation
|
|
192
195
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
193
196
|
outlineAmbientShadowColor = Color.BLACK
|
|
194
197
|
outlineSpotShadowColor = Color.BLACK
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
200
|
|
|
201
|
+
/**
|
|
202
|
+
* The bar draws without its shadow, at the level of the screens, in a transition that draws it with a screen: a
|
|
203
|
+
* screen drawn after it covers it, and an alpha layer around it fades it (with an elevation the renderer would draw
|
|
204
|
+
* the bar over every screen and out of the layer).
|
|
205
|
+
*/
|
|
206
|
+
var isFlat = false
|
|
207
|
+
set(value) {
|
|
208
|
+
if (field == value) return
|
|
209
|
+
field = value
|
|
210
|
+
elevation = if (value) 0f else shadow
|
|
211
|
+
}
|
|
212
|
+
|
|
198
213
|
/** The line under the bar: [height] pixels of [color]. */
|
|
199
214
|
fun setBorder(color: Int, height: Int) {
|
|
200
215
|
border.setBackgroundColor(color)
|
|
@@ -335,8 +350,15 @@ class TopBar(context: Context) : FrameLayout(themed(context)) {
|
|
|
335
350
|
* A picture of the bar under its row — the background, the large title, the search, the line — and where it starts
|
|
336
351
|
* in the bar, for a push to a screen without a large title; none when the bar can't be drawn in software.
|
|
337
352
|
*/
|
|
338
|
-
fun pictureUnderRow(): Pair<Bitmap, Int>?
|
|
339
|
-
|
|
353
|
+
fun pictureUnderRow(): Pair<Bitmap, Int>? = pictureFrom(content.top + rowArea.bottom)
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* A picture of the whole bar, for the screen it leaves with in a transition of the stack's own; none when the bar
|
|
357
|
+
* can't be drawn in software.
|
|
358
|
+
*/
|
|
359
|
+
fun picture(): Bitmap? = pictureFrom(0)?.first
|
|
360
|
+
|
|
361
|
+
private fun pictureFrom(top: Int): Pair<Bitmap, Int>? {
|
|
340
362
|
val width = width
|
|
341
363
|
val height = height - top
|
|
342
364
|
if (width <= 0 || height <= 0) return null
|
package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt
CHANGED
|
@@ -37,6 +37,7 @@ import dev.apexnative.navigation.utils.MaterialTheme
|
|
|
37
37
|
import dev.apexnative.navigation.utils.TestIds
|
|
38
38
|
import dev.apexnative.navigation.utils.UiUtils
|
|
39
39
|
import java.util.WeakHashMap
|
|
40
|
+
import kotlin.math.roundToInt
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* The buttons of one side of the bar in its toolbar: each button a menu item — a text, an icon (on its background), or
|
|
@@ -171,7 +172,7 @@ internal class ButtonBar(
|
|
|
171
172
|
options.fontSize.get(0.0).toFloat(),
|
|
172
173
|
context.resources.displayMetrics,
|
|
173
174
|
)
|
|
174
|
-
spannable.setSpan(AbsoluteSizeSpan(size.
|
|
175
|
+
spannable.setSpan(AbsoluteSizeSpan(size.roundToInt()), 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
175
176
|
}
|
|
176
177
|
if (options.font.hasValue()) {
|
|
177
178
|
val typeface = options.font.getTypeface(typefaces, Typeface.DEFAULT)
|
|
@@ -207,8 +208,8 @@ internal class ButtonBar(
|
|
|
207
208
|
icon,
|
|
208
209
|
color,
|
|
209
210
|
UiUtils.dpToPx(context, background.cornerRadius.get(0.0).toFloat()),
|
|
210
|
-
UiUtils.dpToPx(context, background.width.get(0.0).toFloat()).
|
|
211
|
-
UiUtils.dpToPx(context, background.height.get(0.0).toFloat()).
|
|
211
|
+
UiUtils.dpToPx(context, background.width.get(0.0).toFloat()).roundToInt(),
|
|
212
|
+
UiUtils.dpToPx(context, background.height.get(0.0).toFloat()).roundToInt(),
|
|
212
213
|
)
|
|
213
214
|
}
|
|
214
215
|
|
package/cli/app.js
CHANGED
|
@@ -9,7 +9,7 @@ const { exec } = require('./exec');
|
|
|
9
9
|
const MINIMUM = [0, 85];
|
|
10
10
|
|
|
11
11
|
/** The newest React Native the navigation was checked with: `init` makes apps of its newest patch. */
|
|
12
|
-
const TESTED = [0,
|
|
12
|
+
const TESTED = [0, 88];
|
|
13
13
|
|
|
14
14
|
/** The name the navigation is installed under. */
|
|
15
15
|
const PACKAGE = '@apexnative/navigation';
|
package/cli/project.js
CHANGED
|
@@ -8,8 +8,9 @@ const { exec } = require('./exec');
|
|
|
8
8
|
/**
|
|
9
9
|
* The files of the app the CLI sets up, checks and adds to, where the app has them; `undefined` for one it couldn't
|
|
10
10
|
* find. `styles` is the file of the app's Android theme, `styles.xml` or `themes.xml`; `manifest` its
|
|
11
|
-
* `AndroidManifest.xml`. `
|
|
12
|
-
*
|
|
11
|
+
* `AndroidManifest.xml`. `sceneDelegate` is the `SceneDelegate.swift` next to `AppDelegate.swift`, which apps made
|
|
12
|
+
* from React Native 0.88's template have. `xcodeProject` is the app's `project.pbxproj`; `androidPackage` the package
|
|
13
|
+
* of its Kotlin code (the `namespace` of `android/app/build.gradle`), and `androidSources` the folder it is in.
|
|
13
14
|
* @typedef {{
|
|
14
15
|
* root: string;
|
|
15
16
|
* mainApplication?: string;
|
|
@@ -17,6 +18,7 @@ const { exec } = require('./exec');
|
|
|
17
18
|
* styles?: string;
|
|
18
19
|
* manifest?: string;
|
|
19
20
|
* appDelegate?: string;
|
|
21
|
+
* sceneDelegate?: string;
|
|
20
22
|
* infoPlist?: string;
|
|
21
23
|
* entry?: string;
|
|
22
24
|
* app?: string;
|
|
@@ -64,6 +66,8 @@ function findProject(root, run = exec) {
|
|
|
64
66
|
existing(path.join(androidMain, 'res', 'values', 'themes.xml')),
|
|
65
67
|
manifest: existing(path.join(androidMain, 'AndroidManifest.xml')),
|
|
66
68
|
appDelegate,
|
|
69
|
+
sceneDelegate:
|
|
70
|
+
appDelegate && existing(path.join(path.dirname(appDelegate), 'SceneDelegate.swift')),
|
|
67
71
|
infoPlist: appDelegate && existing(path.join(path.dirname(appDelegate), 'Info.plist')),
|
|
68
72
|
entry: ['index.js', 'index.ts', 'index.tsx']
|
|
69
73
|
.map((name) => path.join(root, name))
|
package/cli/setup/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const { planEntry, navigationFile, HOW_TO: ENTRY_HOW_TO } = require('./steps/ent
|
|
|
16
16
|
const { planEnv, targetOf } = require('./steps/env');
|
|
17
17
|
const { planInfoPlist } = require('./steps/infoPlist');
|
|
18
18
|
const { planMainActivity } = require('./steps/mainActivity');
|
|
19
|
+
const { planSceneDelegate } = require('./steps/sceneDelegate');
|
|
19
20
|
const { planMainApplication } = require('./steps/mainApplication');
|
|
20
21
|
const { planManifest } = require('./steps/manifest');
|
|
21
22
|
const { planStyles } = require('./steps/styles');
|
|
@@ -83,6 +84,18 @@ function planProject(project) {
|
|
|
83
84
|
planAppDelegate,
|
|
84
85
|
notFound('AppDelegate.swift')
|
|
85
86
|
),
|
|
87
|
+
// React Native 0.88's template starts React Native in its scene delegate; the earlier templates have none.
|
|
88
|
+
...(project.sceneDelegate
|
|
89
|
+
? [
|
|
90
|
+
planFile(
|
|
91
|
+
'SceneDelegate.swift',
|
|
92
|
+
'ios',
|
|
93
|
+
project.sceneDelegate,
|
|
94
|
+
planSceneDelegate,
|
|
95
|
+
notFound('SceneDelegate.swift')
|
|
96
|
+
),
|
|
97
|
+
]
|
|
98
|
+
: []),
|
|
86
99
|
planFile('Info.plist', 'ios', project.infoPlist, planInfoPlist, notFound('Info.plist')),
|
|
87
100
|
planFile('index.js', 'js', project.entry, planEntry, ENTRY_HOW_TO),
|
|
88
101
|
];
|
|
@@ -19,7 +19,10 @@ its delegate (the bundle URL) before calling super.
|
|
|
19
19
|
self.reactNativeDelegate = ReactNativeDelegate()
|
|
20
20
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
21
21
|
}
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ReactNativeDelegate is the app's RCTDefaultReactNativeFactoryDelegate, which gives the bundle URL (bundleURL()); in
|
|
25
|
+
React Native 0.88's template that method is in SceneDelegate.`;
|
|
23
26
|
|
|
24
27
|
// Lines of the template's launch: ANNAppDelegate does all of it.
|
|
25
28
|
const TEMPLATE_LINES = [
|
|
@@ -42,6 +45,20 @@ const LAUNCH_OVERRIDE =
|
|
|
42
45
|
const DELEGATE = /^([ \t]*)let\s+delegate\s*=\s*ReactNativeDelegate\(\)[ \t]*$/m;
|
|
43
46
|
const RETURN_TRUE = /^([ \t]*)return\s+true[ \t]*$/m;
|
|
44
47
|
|
|
48
|
+
// React Native 0.88's template starts React Native in its scene delegate: the app delegate only returns true. The
|
|
49
|
+
// bundle URL moves into a delegate of React Native's factory, as the earlier templates had it.
|
|
50
|
+
const REACT_NATIVE_DELEGATE = `class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
|
|
51
|
+
override func bundleURL() -> URL? {
|
|
52
|
+
#if DEBUG
|
|
53
|
+
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
|
54
|
+
#else
|
|
55
|
+
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
56
|
+
#endif
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
const RETURN_TRUE_OR_TRUE = /^([ \t]*)(?:return\s+)?true[ \t]*$/m;
|
|
61
|
+
|
|
45
62
|
/**
|
|
46
63
|
* AppDelegate.swift: the app delegate becomes an ANNAppDelegate.
|
|
47
64
|
* @param {string} text
|
|
@@ -55,13 +72,30 @@ function planAppDelegate(text) {
|
|
|
55
72
|
note: 'changed by hand, see below',
|
|
56
73
|
howTo: HOW_TO,
|
|
57
74
|
});
|
|
58
|
-
if (!HEADER.test(text) || !LAUNCH.test(text)
|
|
75
|
+
if (!HEADER.test(text) || !LAUNCH.test(text)) return manual;
|
|
76
|
+
let next = DELEGATE.test(text) ? fromFactoryLaunch(text) : fromSceneLaunch(text);
|
|
77
|
+
if (next === undefined) return manual;
|
|
59
78
|
|
|
79
|
+
// The dependency provider is ANNAppDelegate's now.
|
|
80
|
+
if (!/\bRCTAppDependencyProvider\b/.test(next)) {
|
|
81
|
+
next = next.replace(/^import\s+ReactAppDependencyProvider[ \t]*\n/m, '');
|
|
82
|
+
}
|
|
83
|
+
next = addImport(next, 'import ApexNavigation', { sorted: false });
|
|
84
|
+
return { status: 'change', next, note: 'ANNAppDelegate' };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The app delegate of the templates before React Native 0.88, which makes React Native's factory and window in its
|
|
89
|
+
* launch; undefined when the launch has code of the app's own that uses them.
|
|
90
|
+
* @param {string} text
|
|
91
|
+
* @returns {string | undefined}
|
|
92
|
+
*/
|
|
93
|
+
function fromFactoryLaunch(text) {
|
|
60
94
|
let next = text
|
|
61
95
|
.replace(HEADER, 'class $1: ANNAppDelegate {')
|
|
62
96
|
.replace(LAUNCH, '$1override func$2');
|
|
63
97
|
for (const line of TEMPLATE_LINES) {
|
|
64
|
-
if (!line.test(next)) return
|
|
98
|
+
if (!line.test(next)) return undefined;
|
|
65
99
|
next = next.replace(line, '');
|
|
66
100
|
}
|
|
67
101
|
next = next.replace(DELEGATE, '$1self.reactNativeDelegate = ReactNativeDelegate()');
|
|
@@ -69,10 +103,10 @@ function planAppDelegate(text) {
|
|
|
69
103
|
// The launch now ends in super's, which starts React Native.
|
|
70
104
|
const launchAt = next.search(LAUNCH_OVERRIDE);
|
|
71
105
|
const returnAt = next.slice(launchAt).search(RETURN_TRUE);
|
|
72
|
-
if (returnAt < 0) return
|
|
106
|
+
if (returnAt < 0) return undefined;
|
|
73
107
|
const body = next.slice(launchAt, launchAt + returnAt).replace(/self\.reactNativeDelegate/g, '');
|
|
74
108
|
// Code of the app's own that used the template's factory, delegate or window: it has to move by hand.
|
|
75
|
-
if (/\b(factory|delegate|window)\b/.test(body)) return
|
|
109
|
+
if (/\b(factory|delegate|window)\b/.test(body)) return undefined;
|
|
76
110
|
const rest = next
|
|
77
111
|
.slice(launchAt + returnAt)
|
|
78
112
|
.replace(
|
|
@@ -82,17 +116,38 @@ function planAppDelegate(text) {
|
|
|
82
116
|
next = next.slice(0, launchAt + returnAt) + rest;
|
|
83
117
|
|
|
84
118
|
// The blank lines the template's launch leaves behind.
|
|
85
|
-
|
|
119
|
+
return next
|
|
86
120
|
.replace(/(class\s+\w+:\s*ANNAppDelegate\s*\{\n)(?:[ \t]*\n)+/, '$1')
|
|
87
121
|
.replace(/(self\.reactNativeDelegate = ReactNativeDelegate\(\)\n)(?:[ \t]*\n)+/, '$1')
|
|
88
122
|
.replace(/\n(?:[ \t]*\n)+([ \t]*return super\.application\()/, '\n$1');
|
|
123
|
+
}
|
|
89
124
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
125
|
+
/**
|
|
126
|
+
* The app delegate of React Native 0.88's template, whose launch only returns true; undefined when the app delegate
|
|
127
|
+
* makes React Native or a window of its own.
|
|
128
|
+
* @param {string} text
|
|
129
|
+
* @returns {string | undefined}
|
|
130
|
+
*/
|
|
131
|
+
function fromSceneLaunch(text) {
|
|
132
|
+
if (/\b(RCTReactNativeFactory|ReactNativeDelegate|window)\b/.test(text)) return undefined;
|
|
133
|
+
let next = text
|
|
134
|
+
.replace(HEADER, 'class $1: ANNAppDelegate {')
|
|
135
|
+
.replace(LAUNCH, '$1override func$2');
|
|
136
|
+
const launchAt = next.search(LAUNCH_OVERRIDE);
|
|
137
|
+
const returnAt = next.slice(launchAt).search(RETURN_TRUE_OR_TRUE);
|
|
138
|
+
if (returnAt < 0) return undefined;
|
|
139
|
+
const rest = next
|
|
140
|
+
.slice(launchAt + returnAt)
|
|
141
|
+
.replace(
|
|
142
|
+
RETURN_TRUE_OR_TRUE,
|
|
143
|
+
'$1self.reactNativeDelegate = ReactNativeDelegate()\n' +
|
|
144
|
+
'$1return super.application(application, didFinishLaunchingWithOptions: launchOptions)'
|
|
145
|
+
);
|
|
146
|
+
next = `${next.slice(0, launchAt + returnAt)}${rest.trimEnd()}\n\n${REACT_NATIVE_DELEGATE}`;
|
|
147
|
+
for (const module of ['React', 'React_RCTAppDelegate']) {
|
|
148
|
+
next = addImport(next, `import ${module}`, { sorted: false });
|
|
93
149
|
}
|
|
94
|
-
|
|
95
|
-
return { status: 'change', next, note: 'ANNAppDelegate' };
|
|
150
|
+
return next;
|
|
96
151
|
}
|
|
97
152
|
|
|
98
153
|
module.exports = { planAppDelegate };
|