@apexnative/navigation 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +22 -0
- package/android/CLAUDE.md +26 -9
- package/android/CORE.md +32 -17
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +25 -9
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +33 -22
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +10 -5
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +47 -46
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -4
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +25 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +8 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +8 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +61 -15
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +25 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +85 -29
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +5 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +14 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +56 -21
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/cli/doctor/checks/app.js +61 -13
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +6 -7
- package/docs-md/containers/modals.md +2 -2
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/help/troubleshooting.md +22 -5
- package/docs-md/index.json +4 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +24 -2
- package/docs-md/start/installation.md +4 -2
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +18 -13
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +28 -30
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNErrorHandler.h +1 -1
- package/ios/Core/ANNErrorHandler.mm +3 -1
- package/ios/Core/ANNEventEmitter.h +1 -1
- package/ios/Core/ANNEventEmitter.mm +2 -2
- package/ios/Core/ANNJavaScriptCalls.h +34 -0
- package/ios/Core/ANNJavaScriptCalls.mm +61 -0
- package/ios/Core/ANNNavigationState.h +7 -5
- package/ios/Core/ANNNavigationState.mm +8 -7
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNRenderWait.h +15 -1
- package/ios/Core/ANNRenderWait.mm +119 -17
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
- package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
- package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
- package/ios/Layouts/Stack/ANNPreviewPush.h +5 -5
- package/ios/Layouts/Stack/ANNPreviewPush.mm +85 -22
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.mm +26 -3
- package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/Image.h +11 -2
- package/ios/Options/Params/Image.mm +15 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +11 -5
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/TopBar/ANNBarButtons.h +9 -5
- package/ios/TopBar/ANNBarButtons.mm +211 -63
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
- package/ios/TopBar/ANNSearchPresenter.mm +170 -19
- package/ios/TopBar/ANNTopBarPresenter.h +5 -0
- package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
- package/ios/TurboModules/ANNTurboModule.mm +39 -21
- package/lib/module/ARCHITECTURE.md +18 -18
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/engine/commands.js +9 -7
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +13 -9
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/options.js +44 -11
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/places.js +52 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/rememberedOptions.js +1 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +22 -12
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +7 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +19 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +23 -4
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +9 -0
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +18 -18
- package/src/adapters/NativeANNTurboModule.ts +5 -2
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +1 -1
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +65 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/rememberedOptions.ts +4 -1
- package/src/engine/screens.tsx +23 -13
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/Events.ts +19 -10
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +24 -4
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/topBar.ts +22 -9
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
package/src/engine/options.ts
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { DynamicColorIOS, Image, Platform, processColor } from 'react-native';
|
|
2
2
|
import type { ColorValue, ImageSourcePropType } from 'react-native';
|
|
3
3
|
import { nextId } from './ids';
|
|
4
|
-
import { rememberButtonRoot, setProps } from './props';
|
|
4
|
+
import { rememberButtonRoot, rememberOptionsComponent, setProps } from './props';
|
|
5
5
|
|
|
6
6
|
type Fields = Record<string, unknown>;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* The options as the native side reads them: colors in the format of the platform, images resolved, and every
|
|
10
10
|
* component in them with an id of its own (its props stay in JavaScript). Options come in their own form only:
|
|
11
|
-
* animations with `enter` and `exit`, `searchBar` and `interpolation` as objects.
|
|
11
|
+
* animations with `enter` and `exit`, `searchBar` and `interpolation` as objects. `owner` is the id of the node the
|
|
12
|
+
* options are for, or `'defaultOptions'`: a component without an id of its own gets one made of the owner, its place
|
|
13
|
+
* in the options and its name, the same for every `mergeOptions` of the node, so the native side keeps the mounted
|
|
14
|
+
* component and only its props change. Without an owner each conversion gives it a new one.
|
|
12
15
|
*/
|
|
13
|
-
export function toNativeOptions<T>(options: T): T {
|
|
14
|
-
return convert(options) as T;
|
|
16
|
+
export function toNativeOptions<T>(options: T, owner?: string): T {
|
|
17
|
+
return convert(options, undefined, { owner, path: '' }) as T;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Where a value is in the options being converted: the node they are for, and the path to the value
|
|
21
|
+
// (`topBar.rightButtons[0].component`).
|
|
22
|
+
interface Place {
|
|
23
|
+
owner: string | undefined;
|
|
24
|
+
path: string;
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
/**
|
|
@@ -35,11 +45,16 @@ function isGroup(value: unknown): value is Fields {
|
|
|
35
45
|
return isFields(value) && !Array.isArray(value);
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
function convert(value: unknown, key
|
|
48
|
+
function convert(value: unknown, key: string | undefined, place: Place): unknown {
|
|
39
49
|
if (key !== undefined && isColor(key)) return nativeColor(value);
|
|
40
50
|
if (key !== undefined && isImage(key)) return nativeImage(value);
|
|
41
51
|
if (Array.isArray(value)) {
|
|
42
|
-
|
|
52
|
+
// An item is known by its id where it has one (a button, a suggestion), so a component keeps its root when the
|
|
53
|
+
// list around it changes; by its place in the list otherwise.
|
|
54
|
+
const items = value.map((item, index) => {
|
|
55
|
+
const id = isFields(item) && typeof item.id === 'string' && item.id ? item.id : index;
|
|
56
|
+
return convert(item, undefined, { ...place, path: `${place.path}[${id}]` });
|
|
57
|
+
});
|
|
43
58
|
if (key?.endsWith('Buttons') || key === 'buttons') items.forEach(noteButtonRoot);
|
|
44
59
|
return key?.endsWith('Buttons') ? items.map(takeButtonProps) : items;
|
|
45
60
|
}
|
|
@@ -47,9 +62,12 @@ function convert(value: unknown, key?: string): unknown {
|
|
|
47
62
|
const result: Fields = {};
|
|
48
63
|
for (const [name, item] of Object.entries(withSymbols(value))) {
|
|
49
64
|
// The props of a component are its own, not options.
|
|
50
|
-
|
|
65
|
+
const path = place.path ? `${place.path}.${name}` : name;
|
|
66
|
+
result[name] = name === 'passProps' ? item : convert(item, name, { ...place, path });
|
|
51
67
|
}
|
|
52
|
-
return key === 'component' || key === 'resultsComponent'
|
|
68
|
+
return key === 'component' || key === 'resultsComponent'
|
|
69
|
+
? takeComponentProps(result, place)
|
|
70
|
+
: result;
|
|
53
71
|
}
|
|
54
72
|
|
|
55
73
|
function isColor(key: string): boolean {
|
|
@@ -105,13 +123,23 @@ function isFields(value: unknown): value is Fields {
|
|
|
105
123
|
return typeof value === 'object' && value !== null;
|
|
106
124
|
}
|
|
107
125
|
|
|
108
|
-
// A component in the options gets an id; the native side creates it with that id, and its props wait here.
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
// A component in the options gets an id; the native side creates it with that id, and its props wait here. Without
|
|
127
|
+
// an id of its own it is the owner's, the place of the component (`topBar.title`, `topBar.rightButtons[Save]` by the
|
|
128
|
+
// button's id, `topBar.searchBar.resultsComponent`) and its name: another name there is another component.
|
|
129
|
+
function takeComponentProps({ passProps, ...component }: Fields, place: Place): Fields {
|
|
130
|
+
const componentId = (component.id as string | undefined) || componentIdAt(place, component.name);
|
|
111
131
|
if (passProps) setProps(componentId, passProps as object);
|
|
112
132
|
return { ...component, componentId };
|
|
113
133
|
}
|
|
114
134
|
|
|
135
|
+
function componentIdAt({ owner, path }: Place, name: unknown): string {
|
|
136
|
+
if (owner === undefined) return nextId('CustomComponent');
|
|
137
|
+
const at = path.endsWith('.component') ? path.slice(0, -'.component'.length) : path;
|
|
138
|
+
const id = `${owner}/${at}/${String(name)}`;
|
|
139
|
+
rememberOptionsComponent(owner, id);
|
|
140
|
+
return id;
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
// The component of a bar button (the top bar's `*Buttons`, the toolbar's `buttons`) is a root the native side only
|
|
116
144
|
// sizes: its wrapper centers the content in the button's slot.
|
|
117
145
|
function noteButtonRoot(button: unknown): void {
|
package/src/engine/places.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SplitViewColumnsStackedEvent } from '../interfaces/Events';
|
|
2
2
|
import type { LayoutNode } from './layout';
|
|
3
3
|
import { onNativeEvent } from './native';
|
|
4
|
+
import { forgetProps } from './props';
|
|
4
5
|
import { screenNameOf } from './slots';
|
|
5
6
|
|
|
6
7
|
// Where each screen is, noted as the engine builds the layouts of the commands: what it shows in and its place in its
|
|
@@ -51,6 +52,11 @@ const stackedSplits = new Set<string>();
|
|
|
51
52
|
let listeningToSplitViews = false;
|
|
52
53
|
// The primaries of pairs shown alone, noted as their node is built, until their place is noted.
|
|
53
54
|
const alone = new Set<string>();
|
|
55
|
+
// The layouts each screen shows in (its stack, pair, tabs, menu, split view, and the root of its layer), and how many
|
|
56
|
+
// live screens each layout holds. A layout whose last screen went is gone, and with it the props of the components the
|
|
57
|
+
// library named in its options: they stay as long as any of its screens may show them again.
|
|
58
|
+
const layoutsOfScreen = new Map<string, readonly string[]>();
|
|
59
|
+
const screensInLayout = new Map<string, number>();
|
|
54
60
|
// The last screens that went, so a command from one of them can say so; a screen the engine never saw is not one.
|
|
55
61
|
const gone = new Set<string>();
|
|
56
62
|
const GONE_KEPT = 200;
|
|
@@ -59,6 +65,15 @@ export function placeOf(screenId: string): ScreenPlace | undefined {
|
|
|
59
65
|
return places.get(screenId);
|
|
60
66
|
}
|
|
61
67
|
|
|
68
|
+
/** The first screen of a stack, as the engine noted it; with a pair first, its primary. */
|
|
69
|
+
export function firstScreenOf(stack: string): string | undefined {
|
|
70
|
+
for (const [id, place] of places) {
|
|
71
|
+
if (place.stack === stack && place.index === 0 && place.arrangement?.placement !== 'secondary')
|
|
72
|
+
return id;
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
/** Notes that a screen is the primary of a pair shown alone, where the native side shows no pairs. */
|
|
63
78
|
export function noteAlone(screenId: string): void {
|
|
64
79
|
alone.add(screenId);
|
|
@@ -69,9 +84,15 @@ export function isGone(screenId: string): boolean {
|
|
|
69
84
|
return gone.has(screenId);
|
|
70
85
|
}
|
|
71
86
|
|
|
87
|
+
/** The layouts the screen shows in, outermost first: those whose screens come with it in a command go in it too. */
|
|
88
|
+
export function layoutsOf(screenId: string): readonly string[] {
|
|
89
|
+
return layoutsOfScreen.get(screenId) ?? [];
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
/**
|
|
73
93
|
* Notes the screens of a layout, the first of a stack at `index`. A modal or an overlay given as a whole is a layer of
|
|
74
|
-
* its own; screens added to one, such as a push, take the layer of the screen they come from
|
|
94
|
+
* its own; screens added to one, such as a push, take the layer of the screen they come from, and `within`, the
|
|
95
|
+
* layouts they show in (`layoutsOf` that screen).
|
|
75
96
|
*/
|
|
76
97
|
export function notePlaces(
|
|
77
98
|
node: LayoutNode,
|
|
@@ -79,12 +100,14 @@ export function notePlaces(
|
|
|
79
100
|
stack: string | null = null,
|
|
80
101
|
index = 0,
|
|
81
102
|
menu?: ScreenPlace['menu'],
|
|
82
|
-
layer: string = container === 'root' ? 'root' : node.id
|
|
103
|
+
layer: string = container === 'root' ? 'root' : node.id,
|
|
104
|
+
within: readonly string[] = []
|
|
83
105
|
): void {
|
|
84
106
|
if (node.type === 'Component' || node.type === 'ExternalComponent') {
|
|
85
107
|
const name = node.data.name ?? '';
|
|
86
108
|
gone.delete(node.id);
|
|
87
109
|
columnOfScreen.delete(node.id);
|
|
110
|
+
noteLayouts(node.id, within);
|
|
88
111
|
places.set(node.id, {
|
|
89
112
|
name: screenNameOf(name),
|
|
90
113
|
container,
|
|
@@ -99,7 +122,7 @@ export function notePlaces(
|
|
|
99
122
|
} else if (node.type === 'Arrangement') {
|
|
100
123
|
// One entry of its stack: both screens have its place there, and each knows the pair.
|
|
101
124
|
node.children.forEach((child, childIndex) => {
|
|
102
|
-
notePlaces(child, container, stack, index, menu, layer);
|
|
125
|
+
notePlaces(child, container, stack, index, menu, layer, [...within, node.id]);
|
|
103
126
|
const place = places.get(child.id);
|
|
104
127
|
if (place) {
|
|
105
128
|
const placement = childIndex === 0 ? 'primary' : 'secondary';
|
|
@@ -109,7 +132,7 @@ export function notePlaces(
|
|
|
109
132
|
} else if (node.type === 'Stack') {
|
|
110
133
|
columnOfStack.delete(node.id);
|
|
111
134
|
node.children.forEach((child, childIndex) =>
|
|
112
|
-
notePlaces(child, container, node.id, childIndex, menu, layer)
|
|
135
|
+
notePlaces(child, container, node.id, childIndex, menu, layer, [...within, node.id])
|
|
113
136
|
);
|
|
114
137
|
} else if (node.type === 'SplitView') {
|
|
115
138
|
// A split view sent again with its id starts anew: its columns are noted again, and it isn't one stack until the
|
|
@@ -118,7 +141,7 @@ export function notePlaces(
|
|
|
118
141
|
const columns = node.data.columns ?? [];
|
|
119
142
|
const stacks: Partial<Record<string, string>> = {};
|
|
120
143
|
node.children.forEach((child, childIndex) => {
|
|
121
|
-
notePlaces(child, container, null, 0, menu, layer);
|
|
144
|
+
notePlaces(child, container, null, 0, menu, layer, [...within, node.id]);
|
|
122
145
|
const column = columns[childIndex];
|
|
123
146
|
if (!column) return;
|
|
124
147
|
if (child.type === 'Stack') stacks[column] = child.id;
|
|
@@ -128,12 +151,46 @@ export function notePlaces(
|
|
|
128
151
|
} else if (node.type === 'SideMenuRoot') {
|
|
129
152
|
for (const part of node.children) {
|
|
130
153
|
const side = MENU_PARTS[part.type];
|
|
154
|
+
const parts = [...within, node.id, part.id];
|
|
131
155
|
part.children.forEach((child) =>
|
|
132
|
-
notePlaces(
|
|
156
|
+
notePlaces(
|
|
157
|
+
child,
|
|
158
|
+
container,
|
|
159
|
+
null,
|
|
160
|
+
0,
|
|
161
|
+
side ? { id: node.id, part: side } : menu,
|
|
162
|
+
layer,
|
|
163
|
+
parts
|
|
164
|
+
)
|
|
133
165
|
);
|
|
134
166
|
}
|
|
135
167
|
} else {
|
|
136
|
-
node.children.forEach((child) =>
|
|
168
|
+
node.children.forEach((child) =>
|
|
169
|
+
notePlaces(child, container, null, 0, menu, layer, [...within, node.id])
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// A screen noted again under its id (a layout sent again) leaves the layouts it was in before.
|
|
175
|
+
function noteLayouts(screenId: string, within: readonly string[]): void {
|
|
176
|
+
leaveLayouts(screenId);
|
|
177
|
+
const layouts = [...new Set(within)];
|
|
178
|
+
layoutsOfScreen.set(screenId, layouts);
|
|
179
|
+
for (const layout of layouts) screensInLayout.set(layout, (screensInLayout.get(layout) ?? 0) + 1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function leaveLayouts(screenId: string): void {
|
|
183
|
+
const layouts = layoutsOfScreen.get(screenId);
|
|
184
|
+
if (!layouts) return;
|
|
185
|
+
layoutsOfScreen.delete(screenId);
|
|
186
|
+
for (const layout of layouts) {
|
|
187
|
+
const count = (screensInLayout.get(layout) ?? 1) - 1;
|
|
188
|
+
if (count > 0) {
|
|
189
|
+
screensInLayout.set(layout, count);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
screensInLayout.delete(layout);
|
|
193
|
+
forgetProps(layout);
|
|
137
194
|
}
|
|
138
195
|
}
|
|
139
196
|
|
|
@@ -198,6 +255,7 @@ export function listenToSplitViews(): void {
|
|
|
198
255
|
|
|
199
256
|
/** The screen is gone. */
|
|
200
257
|
export function forgetPlace(screenId: string): void {
|
|
258
|
+
leaveLayouts(screenId);
|
|
201
259
|
places.delete(screenId);
|
|
202
260
|
columnOfScreen.delete(screenId);
|
|
203
261
|
gone.add(screenId);
|
package/src/engine/props.ts
CHANGED
|
@@ -7,6 +7,11 @@ const watchers = new Map<string, Set<() => void>>();
|
|
|
7
7
|
const none = {};
|
|
8
8
|
// The roots that are the components of bar buttons: their wrapper centers the content in the button's slot.
|
|
9
9
|
const buttonRoots = new Set<string>();
|
|
10
|
+
// The ids the library gave components in options, by the node whose options they are in. Such an id stays the same
|
|
11
|
+
// for every show of the node and may have several roots at once (a component of the default or a stack's options):
|
|
12
|
+
// its props go with the node, not with a root.
|
|
13
|
+
const optionComponents = new Map<string, Set<string>>();
|
|
14
|
+
const optionComponentIds = new Set<string>();
|
|
10
15
|
|
|
11
16
|
export function setProps(id: string, props: object | undefined): void {
|
|
12
17
|
if (props === undefined) byId.delete(id);
|
|
@@ -38,8 +43,32 @@ export function isButtonRoot(id: string): boolean {
|
|
|
38
43
|
return buttonRoots.has(id);
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
/**
|
|
46
|
+
/** Notes an id the library gave a component in the options of the node `owner`. */
|
|
47
|
+
export function rememberOptionsComponent(owner: string, id: string): void {
|
|
48
|
+
let ids = optionComponents.get(owner);
|
|
49
|
+
if (!ids) optionComponents.set(owner, (ids = new Set()));
|
|
50
|
+
ids.add(id);
|
|
51
|
+
optionComponentIds.add(id);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Whether the library gave the id to a component in options: its props go with the node, not with a root. */
|
|
55
|
+
export function isOptionsComponent(id: string): boolean {
|
|
56
|
+
return optionComponentIds.has(id);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The view is gone: its props, and whether it is a button's root, are not needed again; nor are those of the
|
|
61
|
+
* components the library named in its options.
|
|
62
|
+
*/
|
|
42
63
|
export function forgetProps(id: string): void {
|
|
43
64
|
byId.delete(id);
|
|
44
65
|
buttonRoots.delete(id);
|
|
66
|
+
const ids = optionComponents.get(id);
|
|
67
|
+
if (!ids) return;
|
|
68
|
+
optionComponents.delete(id);
|
|
69
|
+
for (const component of ids) {
|
|
70
|
+
byId.delete(component);
|
|
71
|
+
buttonRoots.delete(component);
|
|
72
|
+
optionComponentIds.delete(component);
|
|
73
|
+
}
|
|
45
74
|
}
|
|
@@ -60,7 +60,10 @@ export function rootGoesOut(nodes: LayoutNode[], again: SentRoot | undefined): v
|
|
|
60
60
|
pairNodes(before.nodes, nodes, (old, node) => {
|
|
61
61
|
const options = remembered.get(old.id);
|
|
62
62
|
if (!options) return;
|
|
63
|
-
node.data.options = mergeNativeOptions(
|
|
63
|
+
node.data.options = mergeNativeOptions(
|
|
64
|
+
node.data.options ?? {},
|
|
65
|
+
toNativeOptions(options, node.id)
|
|
66
|
+
);
|
|
64
67
|
moved.set(node.id, options);
|
|
65
68
|
});
|
|
66
69
|
}
|
package/src/engine/screens.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import React, { type ComponentType, useCallback, useEffect, useSyncExternalStore
|
|
|
2
2
|
import { AppRegistry } from 'react-native';
|
|
3
3
|
import type { Options } from '../interfaces/Options';
|
|
4
4
|
import { forgetPlace } from './places';
|
|
5
|
-
import { forgetProps, propsOf, watchProps } from './props';
|
|
5
|
+
import { forgetProps, isOptionsComponent, propsOf, watchProps } from './props';
|
|
6
6
|
import { forgetMergedOptions } from './rememberedOptions';
|
|
7
7
|
import { hasSlot } from './slots';
|
|
8
8
|
import type { NativeControllerProps } from './nativeScreens';
|
|
@@ -55,11 +55,12 @@ export type ScreenRootProps = { componentId: string } & Record<string, unknown>;
|
|
|
55
55
|
const screens = new Map<string, EngineScreen>();
|
|
56
56
|
const roots = new Map<string, ComponentType<{ componentId: string; rootTag?: number }>>();
|
|
57
57
|
let unmounted: ((screenId: string) => void) | undefined;
|
|
58
|
-
// The React
|
|
59
|
-
// the old screens again in the new runtime, under the ids the new screens get too, and
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
58
|
+
// The React roots mounted under each id, by their root tags. One id can have several: after a JavaScript reload React
|
|
59
|
+
// Native starts the roots of the old screens again in the new runtime, under the ids the new screens get too, and
|
|
60
|
+
// unmounts them once the new ones show; a component of the default or a stack's options has one id and a root in the
|
|
61
|
+
// bar of every screen that shows it. The id lets its props go only with the last of them. A slot of the app's one tree
|
|
62
|
+
// has no tag: while the one tree shows a screen with the id, no root of that id lets it go.
|
|
63
|
+
const owners = new Map<string, Set<number>>();
|
|
63
64
|
|
|
64
65
|
/** What runs when the root of a screen unmounts: the screen is gone. */
|
|
65
66
|
export function onScreenUnmounted(listener: (screenId: string) => void): void {
|
|
@@ -214,17 +215,26 @@ export function screenRoot(
|
|
|
214
215
|
);
|
|
215
216
|
const props = useSyncExternalStore(watch, () => propsOf(componentId));
|
|
216
217
|
useEffect(() => {
|
|
217
|
-
if (rootTag !== undefined
|
|
218
|
-
owners.
|
|
218
|
+
if (rootTag !== undefined) {
|
|
219
|
+
const tags = owners.get(componentId) ?? new Set<number>();
|
|
220
|
+
tags.add(rootTag);
|
|
221
|
+
owners.set(componentId, tags);
|
|
219
222
|
}
|
|
220
223
|
return () => {
|
|
221
|
-
//
|
|
222
|
-
// with the id: a later screen under the id, the restarted root of a screen after a
|
|
223
|
-
// (its slot may mount only once the app's provider lets it), the screen rendering
|
|
224
|
-
|
|
224
|
+
// A root while another root of the id is mounted leaves the screen alone, and so does any root while the one
|
|
225
|
+
// tree still has a slot with the id: a later screen under the id, the restarted root of a screen after a
|
|
226
|
+
// reload into the one tree (its slot may mount only once the app's provider lets it), the screen rendering
|
|
227
|
+
// again under the provider.
|
|
228
|
+
const tags = owners.get(componentId);
|
|
229
|
+
if (rootTag !== undefined && tags) {
|
|
230
|
+
tags.delete(rootTag);
|
|
231
|
+
if (tags.size > 0) return;
|
|
232
|
+
}
|
|
225
233
|
if (hasSlot(componentId)) return;
|
|
226
234
|
owners.delete(componentId);
|
|
227
|
-
|
|
235
|
+
// A component the library named in options keeps its props for the next root under its id (the node shows
|
|
236
|
+
// it again, or another screen shows the same default): they go with the node.
|
|
237
|
+
if (!isOptionsComponent(componentId)) forgetProps(componentId);
|
|
228
238
|
forgetPlace(componentId);
|
|
229
239
|
forgetMergedOptions(componentId);
|
|
230
240
|
unmounted?.(componentId);
|
|
@@ -10,7 +10,7 @@ export type ComponentType =
|
|
|
10
10
|
|
|
11
11
|
/** The fields every appearance event has. */
|
|
12
12
|
interface ComponentAppearanceEvent {
|
|
13
|
-
/** The id of the component: the screen, or the
|
|
13
|
+
/** The id of the component: of the screen, or the bar component's own id. */
|
|
14
14
|
componentId: string;
|
|
15
15
|
/** The name of the component: for a screen, its name without any internal prefix. */
|
|
16
16
|
componentName: string;
|
|
@@ -60,7 +60,7 @@ export interface NavigationButtonPressedEvent {
|
|
|
60
60
|
* `ApexNavigation.back`; the hardware back of a modal has `ApexNavigation.hardwareBackButton`.
|
|
61
61
|
*/
|
|
62
62
|
buttonId: string;
|
|
63
|
-
/** (iOS) For a button with `toggle`: its new state,
|
|
63
|
+
/** (iOS) For a button with `toggle`: its new state, which the screen keeps. */
|
|
64
64
|
selected?: boolean;
|
|
65
65
|
}
|
|
66
66
|
|
package/src/interfaces/Events.ts
CHANGED
|
@@ -54,8 +54,12 @@ export interface NavigationStateChangedEvent {
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* A tab was selected by the user: a press on the tab, a pick from the "More" list or a press on "More" that showed a
|
|
57
|
-
* tab (iOS). It
|
|
58
|
-
* `
|
|
57
|
+
* tab (iOS). It goes right after the `ComponentDidAppear` of the new tab's screen, before the navigation state and
|
|
58
|
+
* before the tab that was left goes back to its first screen (`popToRootOnBlur`), and only when the tab changed: a
|
|
59
|
+
* press on the selected tab or on a `selectTabOnPress: false` tab is reported by `BottomTabPressedEvent` alone.
|
|
60
|
+
* Selecting a tab from code sends nothing. A switch still running when another press, long press or choice from code
|
|
61
|
+
* comes ends first, with this event, so the events of one press never mix with the next; when that tab's screen
|
|
62
|
+
* hasn't drawn yet, this event goes before its held `ComponentDidAppear`.
|
|
59
63
|
*/
|
|
60
64
|
export interface BottomTabSelectedEvent {
|
|
61
65
|
/** The index of the tab that is now selected. */
|
|
@@ -67,24 +71,28 @@ export interface BottomTabSelectedEvent {
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
/**
|
|
70
|
-
* A tab was pressed, the
|
|
71
|
-
* the
|
|
74
|
+
* A tab was pressed, on the bar or on a cell of `bottomTabs.customRow`, the selected one too. It goes first, before
|
|
75
|
+
* anything the press does. Another tab: then the switch's `ComponentWillAppear`, `ComponentDidDisappear` and
|
|
76
|
+
* `ComponentDidAppear`, then `BottomTabSelectedEvent`. The selected tab again (`wasSelected: true`): its stack goes
|
|
77
|
+
* back to its first screen with the events of the user's back (`ScreenPopped`), or, on the first screen, its content
|
|
78
|
+
* scrolls to the top; with `popToRootOnPress: false` nothing follows. With `selectTabOnPress: false` it is the only
|
|
79
|
+
* event and the selection stays. A long press sends `BottomTabLongPressedEvent` instead.
|
|
72
80
|
*/
|
|
73
81
|
export interface BottomTabPressedEvent {
|
|
74
82
|
/** The index of the tab that was pressed. */
|
|
75
83
|
tabIndex: number;
|
|
76
84
|
/** The id of the tabs. */
|
|
77
85
|
bottomTabsId: string;
|
|
78
|
-
/** The id of the screen the tab shows: the top of its stack. */
|
|
86
|
+
/** The id of the screen the tab shows: the top of its stack, or the tab itself when it is a screen. */
|
|
79
87
|
screenId?: string;
|
|
80
88
|
/** Whether the tab was the selected one already; a press on the selected tab scrolls its content to the top. */
|
|
81
89
|
wasSelected: boolean;
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
/**
|
|
85
|
-
* A tab was pressed and held
|
|
86
|
-
*
|
|
87
|
-
*
|
|
93
|
+
* A tab was pressed and held, on the bar or on a cell of `bottomTabs.customRow`. The tab is not selected and gets no
|
|
94
|
+
* press event. On iOS it is not sent for "More" or for the tab strip at the top of an iPad. On Android it replaces the
|
|
95
|
+
* tooltip of the tab.
|
|
88
96
|
*/
|
|
89
97
|
export interface BottomTabLongPressedEvent {
|
|
90
98
|
/** The index of the tab that was pressed. */
|
|
@@ -195,11 +203,12 @@ export type HingeState = 'closed' | 'partiallyOpen' | 'fullyOpen' | 'unknown';
|
|
|
195
203
|
*/
|
|
196
204
|
export interface HingeChangedEvent {
|
|
197
205
|
/**
|
|
198
|
-
* The state of the hinge. On Android `closed` means the
|
|
206
|
+
* The state of the hinge. On Android `closed` means the app's window has no fold, or the angle is below 90 degrees,
|
|
207
|
+
* so a device open flat with a window off the fold (split screen) reports `closed`; Android never gives `unknown`.
|
|
199
208
|
*/
|
|
200
209
|
state?: HingeState;
|
|
201
210
|
/**
|
|
202
|
-
* The angle of the hinge in degrees
|
|
211
|
+
* The angle of the hinge in degrees, to tenths. Missing when the state is `unknown` and where the system
|
|
203
212
|
* gives no angle.
|
|
204
213
|
*/
|
|
205
214
|
angle?: number;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Every command with a promise (an async method of src/adapters/NativeANNTurboModule.ts): what it resolves with, whether the navigation state goes before its answer (`state`), and whether `ANN.CommandCompleted` follows the answer (`completed`). scripts/contract.mjs makes ios/Contract/ANNCommandContract.{h,mm} and android/.../react/CommandContract.kt from it, and fails when a command of the spec is missing here or a command here is not in the spec.",
|
|
3
|
+
"commands": [
|
|
4
|
+
{
|
|
5
|
+
"name": "setRoot",
|
|
6
|
+
"resolves": "the id of the root, once the root and the layout's modals and overlays show",
|
|
7
|
+
"state": true,
|
|
8
|
+
"completed": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "push",
|
|
12
|
+
"resolves": "the id of the screen that shows; of a pair, the pair's id",
|
|
13
|
+
"state": true,
|
|
14
|
+
"completed": true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "preparePush",
|
|
18
|
+
"resolves": "the frame `{ width, height, padding }` of the prepared screen, or null; always null on iOS",
|
|
19
|
+
"state": false,
|
|
20
|
+
"completed": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "pop",
|
|
24
|
+
"resolves": "the id of the screen that went",
|
|
25
|
+
"state": true,
|
|
26
|
+
"completed": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "popTo",
|
|
30
|
+
"resolves": "the id of the screen it went back to",
|
|
31
|
+
"state": true,
|
|
32
|
+
"completed": true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "popToRoot",
|
|
36
|
+
"resolves": "the id of the first screen of the stack",
|
|
37
|
+
"state": true,
|
|
38
|
+
"completed": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "setStackRoot",
|
|
42
|
+
"resolves": "the id of the new top screen",
|
|
43
|
+
"state": true,
|
|
44
|
+
"completed": true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "showModal",
|
|
48
|
+
"resolves": "the id of the modal's root",
|
|
49
|
+
"state": true,
|
|
50
|
+
"completed": true
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "dismissModal",
|
|
54
|
+
"resolves": "the id of the modal's root",
|
|
55
|
+
"state": true,
|
|
56
|
+
"completed": true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "dismissAllModals",
|
|
60
|
+
"resolves": "an empty string",
|
|
61
|
+
"state": true,
|
|
62
|
+
"completed": true
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "showOverlay",
|
|
66
|
+
"resolves": "the id of the overlay's root",
|
|
67
|
+
"state": true,
|
|
68
|
+
"completed": true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "dismissOverlay",
|
|
72
|
+
"resolves": "the id of the overlay's root",
|
|
73
|
+
"state": true,
|
|
74
|
+
"completed": true
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "dismissAllOverlays",
|
|
78
|
+
"resolves": "an empty string",
|
|
79
|
+
"state": true,
|
|
80
|
+
"completed": true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "showInColumn",
|
|
84
|
+
"resolves": "the id of the screen that shows",
|
|
85
|
+
"state": true,
|
|
86
|
+
"completed": true
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "setColumnShown",
|
|
90
|
+
"resolves": "nothing once the columns stopped moving (`undefined` on iOS, `null` on Android)",
|
|
91
|
+
"state": true,
|
|
92
|
+
"completed": false
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "getLaunchLink",
|
|
96
|
+
"resolves": "the link that launched the app, or null",
|
|
97
|
+
"state": false,
|
|
98
|
+
"completed": false
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
@@ -354,8 +354,11 @@ export interface StackAnimationOptions {
|
|
|
354
354
|
enabled?: boolean;
|
|
355
355
|
/**
|
|
356
356
|
* Whether to wait for the new top screen to be drawn before the transition. Without it, the transition waits for the
|
|
357
|
-
*
|
|
358
|
-
*
|
|
357
|
+
* first mount of the new top's content, half a second at most, with the platform's transition as with an animation
|
|
358
|
+
* of its own; on a push the options the screen sets as it renders for the first time (`useScreenOptions`, such as
|
|
359
|
+
* its title) are in its bar from the first frame. `true` waits as long as it takes, `false` starts at once, the
|
|
360
|
+
* content drawing as it comes ((Android) a push with `appProvider` still renders the screen first). (iOS) The
|
|
361
|
+
* components of the bar are waited for too, each to its own limit.
|
|
359
362
|
*/
|
|
360
363
|
waitForRender?: boolean;
|
|
361
364
|
/**
|
|
@@ -402,6 +405,20 @@ export interface StackPushAnimationOptions extends StackAnimationOptions {
|
|
|
402
405
|
zoom?: ZoomTransitionOptions;
|
|
403
406
|
}
|
|
404
407
|
|
|
408
|
+
/**
|
|
409
|
+
* The animation of a pop, read from the screen that goes. As {@link StackAnimationOptions}, with the preview of the
|
|
410
|
+
* back gesture on Android.
|
|
411
|
+
*/
|
|
412
|
+
export interface StackPopAnimationOptions extends StackAnimationOptions {
|
|
413
|
+
/**
|
|
414
|
+
* (Android 14+) Whether the back gesture on a pushed screen previews its pop as the user swipes: the screen shrinks
|
|
415
|
+
* and follows the finger, the screen under it shows behind. `false` pops the screen when the gesture is released,
|
|
416
|
+
* as on Android 13 and older; back still pops it. iOS ignores it: its swipe back is UIKit's.
|
|
417
|
+
* @default true
|
|
418
|
+
*/
|
|
419
|
+
preview?: boolean;
|
|
420
|
+
}
|
|
421
|
+
|
|
405
422
|
/**
|
|
406
423
|
* The animation of `setStackRoot`, read from the new top screen. As {@link StackAnimationOptions}: without an animation
|
|
407
424
|
* of its own, iOS plays the push animation of the new top screen or UIKit's transition, and Android plays a fade
|
|
@@ -465,8 +482,11 @@ export interface ModalAnimationOptions extends EnterExitAnimationOptions {
|
|
|
465
482
|
export interface AnimationOptions {
|
|
466
483
|
/** The push of a screen, read from the screen that comes. */
|
|
467
484
|
push?: StackPushAnimationOptions;
|
|
468
|
-
/**
|
|
469
|
-
|
|
485
|
+
/**
|
|
486
|
+
* The pop of a screen, also `popTo` and `popToRoot`, read from the screen that goes. The swipe back of iOS is
|
|
487
|
+
* UIKit's; the back gesture of Android previews the pop (`preview`).
|
|
488
|
+
*/
|
|
489
|
+
pop?: StackPopAnimationOptions;
|
|
470
490
|
/** The replacing of the screens of a stack, read from the new top screen. */
|
|
471
491
|
setStackRoot?: SetStackRootAnimationOptions;
|
|
472
492
|
/**
|
|
@@ -138,8 +138,10 @@ export type OptionsTopBarButtonMenuElement =
|
|
|
138
138
|
/**
|
|
139
139
|
* A menu of the top bar: the menu of a button, the overflow menu, the menu of the title. A list of `items` sent with
|
|
140
140
|
* `mergeOptions` replaces the earlier list as a whole, and `items: []` removes the menu; `title` and
|
|
141
|
-
* `singleSelection` merge field by field (iOS). What the user picks in a `singleSelection` menu
|
|
142
|
-
*
|
|
141
|
+
* `singleSelection` merge field by field (iOS). What the user picks in a `singleSelection` menu stays with the screen,
|
|
142
|
+
* through it showing again, a `mergeOptions` that sends the same items again and `setDefaultOptions`; a `mergeOptions`
|
|
143
|
+
* into the screen that changes the checks of that menu's level, or takes the item away, replaces it. A merge into the
|
|
144
|
+
* stack or the tabs around the screen doesn't.
|
|
143
145
|
*/
|
|
144
146
|
export interface OptionsTopBarButtonMenu {
|
|
145
147
|
/** The title above the items. (iOS) Android doesn't show it. */
|
|
@@ -285,8 +287,10 @@ export interface OptionsTopBarButton {
|
|
|
285
287
|
badgeTextColor?: Color;
|
|
286
288
|
/**
|
|
287
289
|
* (iOS) Whether the button is a toggle: every press flips `selected` and sends a press with the new state. The state
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
+
* stays with the screen under the button's id, through it showing again, a `mergeOptions` that sends the same
|
|
291
|
+
* buttons again and `setDefaultOptions`; a `mergeOptions` into the screen that changes `selected` replaces it (a merge
|
|
292
|
+
* into the stack around it doesn't). A button set again
|
|
293
|
+
* without `selected` takes the state of the button with the same id. Not for component buttons,
|
|
290
294
|
* buttons with `iconBackground`, and `showText` buttons.
|
|
291
295
|
*/
|
|
292
296
|
toggle?: boolean;
|
|
@@ -351,7 +355,11 @@ export interface OptionsTopBarButton {
|
|
|
351
355
|
* replaces it.
|
|
352
356
|
*/
|
|
353
357
|
export interface OptionsTopBarButtonComponent {
|
|
354
|
-
/**
|
|
358
|
+
/**
|
|
359
|
+
* The id of the component. Without it, the library gives one made of the screen, the component's place in the
|
|
360
|
+
* options and its `name`: it stays the same for every `mergeOptions` of the screen, so the mounted component stays
|
|
361
|
+
* and only its props change. The same `name` and `id` mean the same component.
|
|
362
|
+
*/
|
|
355
363
|
id?: string;
|
|
356
364
|
/** The name of the registered component. */
|
|
357
365
|
name: string;
|