@apexnative/navigation 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +31 -0
- package/android/CLAUDE.md +38 -9
- package/android/CORE.md +11 -4
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- 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/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/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +2 -24
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +27 -19
- 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/events/AppearanceDispatcher.kt +17 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- 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/SecondaryScreenPresenter.kt +13 -5
- 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 +92 -19
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -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 +79 -13
- 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 +29 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +22 -18
- 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 +45 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +35 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- 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 -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 +72 -16
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- 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 +164 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +136 -40
- 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 +107 -4
- 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/StackSearchPresenter.kt +3 -3
- 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 +131 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- 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 +4 -2
- 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/fab/Fab.kt +14 -10
- 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/cli/doctor/checks/app.js +61 -13
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/docs-md/architecture/transitions.md +12 -7
- 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/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +20 -3
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +8 -2
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +3 -1
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +13 -8
- package/ios/Contract/ANNCommandAnswer.mm +5 -20
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.mm +1 -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 -3
- package/ios/Core/ANNNavigationState.mm +10 -5
- 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/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/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/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.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 +44 -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/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 +5 -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/animations.d.ts +15 -7
- 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/modals.d.ts +18 -13
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +6 -3
- package/lib/typescript/interfaces/options/system.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/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +9 -4
- 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 +56 -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/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +15 -7
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/modals.ts +18 -13
- package/src/interfaces/options/system.ts +6 -3
- package/src/interfaces/options/topBar.ts +22 -9
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
#import "ANNOptionsReader.h"
|
|
3
3
|
#import <React/RCTConvert.h>
|
|
4
4
|
#import <React/RCTLog.h>
|
|
5
|
-
#import <React/RCTUtils.h>
|
|
6
5
|
|
|
7
6
|
@implementation ImageParser
|
|
8
7
|
|
|
@@ -12,10 +11,10 @@
|
|
|
12
11
|
return [[Image alloc] initWithValue:nil];
|
|
13
12
|
}
|
|
14
13
|
NSString *path = [ANNOptionsReader pathOfKey:key];
|
|
15
|
-
return [[Image alloc] initWithValue:[self
|
|
14
|
+
return [[Image alloc] initWithValue:[self sourceOf:value path:path]];
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
+ (
|
|
17
|
+
+ (ANNImageSource *)sourceOf:(id)value path:(NSString *)path {
|
|
19
18
|
if ([value isKindOfClass:NSArray.class]) {
|
|
20
19
|
RCTLogError(@"[ApexNavigation] the image of '%@' is a list of sources, which options don't support: give "
|
|
21
20
|
@"one source",
|
|
@@ -23,27 +22,28 @@
|
|
|
23
22
|
return nil;
|
|
24
23
|
}
|
|
25
24
|
if ([value isKindOfClass:NSDictionary.class] && value[@"system"]) {
|
|
26
|
-
return [self
|
|
25
|
+
return [self symbolOf:value path:path];
|
|
27
26
|
}
|
|
28
27
|
if (![value isKindOfClass:NSString.class] && ![value isKindOfClass:NSDictionary.class]) {
|
|
29
28
|
return nil;
|
|
30
29
|
}
|
|
31
|
-
return [self
|
|
30
|
+
return [self addressOf:value path:path];
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// An SF Symbol; a name the system doesn't have takes the fallback source, read the same way.
|
|
35
|
-
+ (
|
|
34
|
+
+ (ANNImageSource *)symbolOf:(NSDictionary *)value path:(NSString *)path {
|
|
36
35
|
id name = value[@"system"];
|
|
37
|
-
UIImage *image = [name isKindOfClass:NSString.class] ? [UIImage systemImageNamed:name] : nil;
|
|
38
|
-
if (image) {
|
|
39
|
-
return image;
|
|
40
|
-
}
|
|
41
36
|
id fallback = value[@"fallback"];
|
|
42
|
-
|
|
37
|
+
ANNImageSource *fallbackSource =
|
|
38
|
+
fallback && fallback != NSNull.null ? [self sourceOf:fallback path:path] : nil;
|
|
39
|
+
if (![name isKindOfClass:NSString.class] || [name length] == 0) {
|
|
40
|
+
return fallbackSource;
|
|
41
|
+
}
|
|
42
|
+
return [ANNImageSource symbolNamed:name fallback:fallbackSource];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// A resolved source
|
|
46
|
-
+ (
|
|
45
|
+
// A resolved source: its address and the fields its scale comes from.
|
|
46
|
+
+ (ANNImageSource *)addressOf:(id)source path:(NSString *)path {
|
|
47
47
|
NSDictionary *fields = [source isKindOfClass:NSDictionary.class] ? source : nil;
|
|
48
48
|
id address = fields ? (fields[@"uri"] ?: fields[@"url"]) : source;
|
|
49
49
|
if (![address isKindOfClass:NSString.class] || [address length] == 0) {
|
|
@@ -56,48 +56,30 @@
|
|
|
56
56
|
}
|
|
57
57
|
NSURL *url = [RCTConvert NSURL:address];
|
|
58
58
|
NSString *scheme = url.scheme.lowercaseString;
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
id packagerAsset = fields[@"__packager_asset"];
|
|
60
|
+
BOOL metroAsset = ANNJSONIsBoolean(packagerAsset) && [packagerAsset boolValue];
|
|
61
|
+
ANNImageSourceKind kind;
|
|
61
62
|
if ([scheme isEqualToString:@"file"]) {
|
|
62
|
-
|
|
63
|
+
kind = ANNImageSourceKindFile;
|
|
63
64
|
} else if ([scheme isEqualToString:@"data"]) {
|
|
64
|
-
|
|
65
|
+
kind = ANNImageSourceKindData;
|
|
65
66
|
} else if (([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) && metroAsset) {
|
|
66
|
-
|
|
67
|
-
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
|
|
67
|
+
kind = ANNImageSourceKindMetro;
|
|
68
68
|
} else {
|
|
69
69
|
RCTLogError(@"[ApexNavigation] the image of '%@' is not loaded: remote images are not supported in options "
|
|
70
70
|
@"(%@)",
|
|
71
71
|
path, address);
|
|
72
72
|
return nil;
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
+ (UIImage *)image:(UIImage *)image scaledBy:(NSDictionary *)fields {
|
|
84
|
-
id scaleValue = fields[@"scale"];
|
|
85
|
-
id widthValue = fields[@"width"];
|
|
86
|
-
id heightValue = fields[@"height"];
|
|
87
|
-
CGFloat scale = ANNJSONIsNumber(scaleValue) ? [scaleValue doubleValue] : 0;
|
|
88
|
-
CGFloat width = ANNJSONIsNumber(widthValue) ? [widthValue doubleValue] : 0;
|
|
89
|
-
CGFloat height = ANNJSONIsNumber(heightValue) ? [heightValue doubleValue] : 0;
|
|
90
|
-
if (scale <= 0 && width > 0 && image.CGImage) {
|
|
91
|
-
scale = CGImageGetWidth(image.CGImage) / width;
|
|
92
|
-
}
|
|
93
|
-
if (scale > 0 && image.CGImage && scale != image.scale) {
|
|
94
|
-
image = [UIImage imageWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
|
|
95
|
-
}
|
|
96
|
-
if (width > 0 && height > 0 && !CGSizeEqualToSize(image.size, CGSizeMake(width, height))) {
|
|
97
|
-
RCTLogInfo(@"[ApexNavigation] an image of the options is %@, its source says %@",
|
|
98
|
-
NSStringFromCGSize(image.size), NSStringFromCGSize(CGSizeMake(width, height)));
|
|
99
|
-
}
|
|
100
|
-
return image;
|
|
74
|
+
id scale = fields[@"scale"];
|
|
75
|
+
id width = fields[@"width"];
|
|
76
|
+
id height = fields[@"height"];
|
|
77
|
+
return [ANNImageSource sourceOfKind:kind
|
|
78
|
+
url:url
|
|
79
|
+
address:address
|
|
80
|
+
scale:ANNJSONIsNumber(scale) ? [scale doubleValue] : 0
|
|
81
|
+
width:ANNJSONIsNumber(width) ? [width doubleValue] : 0
|
|
82
|
+
height:ANNJSONIsNumber(height) ? [height doubleValue] : 0];
|
|
101
83
|
}
|
|
102
84
|
|
|
103
85
|
@end
|
|
@@ -37,7 +37,7 @@ NS_SWIFT_NAME(NativeScreenContext)
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Runs a command of the navigation from this screen in JS, as `useNavigation()` of a React screen does: push, pop,
|
|
40
|
-
* popTo, popToRoot, showInColumn, showModal, dismiss, showOverlay,
|
|
40
|
+
* popTo, popToRoot, showInColumn, showModal, dismiss, showOverlay, setOptions, selectTab, setTabBadge.
|
|
41
41
|
* `arguments` are those of the JS command; `completion` gets what it resolved with.
|
|
42
42
|
*
|
|
43
43
|
* ```swift
|
package/ios/RECIPES.md
CHANGED
|
@@ -16,9 +16,14 @@ you do exactly that; the map is `ios/CLAUDE.md`.
|
|
|
16
16
|
what the merge names) and in `applyDefaultOptionsChange:`. A system-UI option also goes into
|
|
17
17
|
`+[ANNSystemUI groupsNamedBy:onlyRead:]`; a bar-placement option into `ANNOptionsChangeScreenBars`.
|
|
18
18
|
4. **The cache.** Never invalidate by hand. Own options change through `ANNNodeHelper` (`mergeOwnOptions:`,
|
|
19
|
-
`changeOwnOptions:`)
|
|
20
|
-
`+[
|
|
21
|
-
|
|
19
|
+
`changeOwnOptions:`), the user layer through `setUserChoice:forKey:`, and defaults through
|
|
20
|
+
`+[ANNDefaultOptions setCurrent:]`; all call `+[ANNOptionsCache optionsChanged]`. `changeOwnOptions:` is for what
|
|
21
|
+
the core keeps of the app's merges. A presenter or module that keeps a choice of the user (a toggle, a menu check,
|
|
22
|
+
a scope) puts an `ANNUserChoice` into the node's user layer under a key of its own: its `apply` block writes the
|
|
23
|
+
choice by identity (a button or item by its id) on a copy, its `merged` block says when the app's merge takes it
|
|
24
|
+
away, by Android's rules (`UserChoices.yieldTo`): a value other than the app had there (the own options', else the
|
|
25
|
+
node's resolution before the merge), or what the choice is about gone from the resolution after it. Never into the
|
|
26
|
+
options in place.
|
|
22
27
|
5. **Android** reads the same JSON and has unit tests of parsing and merging; the iOS side has none (see Checks in `ios/CLAUDE.md`).
|
|
23
28
|
6. **The rule:** an option does what its name promises, with the same meaning on both platforms
|
|
24
29
|
(`.claude/rules/general.md`, `.claude/rules/options.md`). A platform that can't, says so in the JSDoc.
|
|
@@ -37,6 +42,7 @@ you do exactly that; the map is `ios/CLAUDE.md`.
|
|
|
37
42
|
thrown with `+[ANNErrorTexts rejection:]`. A new text goes into `src/interfaces/contract/errors.json`, and
|
|
38
43
|
`node scripts/contract.mjs` makes `ANNErrorTexts` (and Android's `ErrorTexts`) from it; never edit them by hand.
|
|
39
44
|
6. **The answer:** end through `ANNAppearanceDispatcher` (`endTransitionHiding:…answer:value:`) or call
|
|
40
|
-
`-[ANNCommandAnswer resolve:]`; the state and `ANN.CommandCompleted` follow by themselves
|
|
41
|
-
|
|
45
|
+
`-[ANNCommandAnswer resolve:]`; the state and `ANN.CommandCompleted` follow by themselves as `ANNCommandContract`
|
|
46
|
+
says. A new command with a promise gets its entry in `src/interfaces/contract/commands.json` (what it resolves
|
|
47
|
+
with, `state`, `completed`), then `node scripts/contract.mjs`.
|
|
42
48
|
7. **Android** gets the same command; its tests check the texts.
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
using namespace facebook::react;
|
|
10
10
|
|
|
11
|
+
@interface ANNSlotComponentView () <ANNSlotView>
|
|
12
|
+
@end
|
|
13
|
+
|
|
11
14
|
@implementation ANNSlotComponentView {
|
|
12
15
|
// The React children live here. It waits hidden inside the slot, then moves into the container.
|
|
13
16
|
ANNSlotContentView *_contentView;
|
|
14
17
|
ANNSlotShadowNode::ConcreteState::Shared _state;
|
|
15
|
-
BOOL _observing;
|
|
16
18
|
// The layout that measures the content was asked for once.
|
|
17
19
|
BOOL _measureAsked;
|
|
18
20
|
}
|
|
@@ -33,19 +35,8 @@ using namespace facebook::react;
|
|
|
33
35
|
return self;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
- (void)dealloc {
|
|
37
|
-
[NSNotificationCenter.defaultCenter removeObserver:self];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
38
|
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps {
|
|
41
39
|
[super updateProps:props oldProps:oldProps];
|
|
42
|
-
if (!_observing) {
|
|
43
|
-
_observing = YES;
|
|
44
|
-
[NSNotificationCenter.defaultCenter addObserver:self
|
|
45
|
-
selector:@selector(containerDidChange:)
|
|
46
|
-
name:ANNSlotContainerDidChangeNotification
|
|
47
|
-
object:nil];
|
|
48
|
-
}
|
|
49
40
|
[self attachContent];
|
|
50
41
|
}
|
|
51
42
|
|
|
@@ -54,13 +45,23 @@ using namespace facebook::react;
|
|
|
54
45
|
[self attachContent];
|
|
55
46
|
}
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
#pragma mark - ANNSlotView
|
|
49
|
+
|
|
50
|
+
- (void)slotContainerDidChange:(NSString *)slotId {
|
|
51
|
+
if ([slotId isEqualToString:self.nativeId]) {
|
|
59
52
|
[self attachContent];
|
|
60
53
|
}
|
|
61
54
|
}
|
|
62
55
|
|
|
56
|
+
#pragma mark - Content
|
|
57
|
+
|
|
63
58
|
- (void)attachContent {
|
|
59
|
+
// The view hears the changes of the container under the slot id it has now, and no other.
|
|
60
|
+
if (self.nativeId != nil) {
|
|
61
|
+
[ANNSlotContainers registerSlotView:self forSlotId:self.nativeId];
|
|
62
|
+
} else {
|
|
63
|
+
[ANNSlotContainers unregisterSlotView:self];
|
|
64
|
+
}
|
|
64
65
|
UIView *container = self.nativeId ? [ANNSlotContainers containerForSlotId:self.nativeId] : nil;
|
|
65
66
|
UIView *target = container ?: self;
|
|
66
67
|
if (_contentView.superview != target) {
|
|
@@ -108,15 +109,21 @@ using namespace facebook::react;
|
|
|
108
109
|
return nil;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
// A recycled slot waits for another screen: it
|
|
112
|
+
// A recycled slot waits for another screen: it leaves its slot id and drops the old screen's state. Its new props put
|
|
113
|
+
// it under the new id.
|
|
112
114
|
- (void)prepareForRecycle {
|
|
113
115
|
[super prepareForRecycle];
|
|
114
|
-
[
|
|
115
|
-
_observing = NO;
|
|
116
|
+
[ANNSlotContainers unregisterSlotView:self];
|
|
116
117
|
_measureAsked = NO;
|
|
117
118
|
_state.reset();
|
|
118
119
|
[_contentView removeFromSuperview];
|
|
119
120
|
_contentView.hidden = YES;
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
// An unmounted slot that Fabric doesn't keep for reuse leaves its slot id too.
|
|
124
|
+
- (void)invalidate {
|
|
125
|
+
[super invalidate];
|
|
126
|
+
[ANNSlotContainers unregisterSlotView:self];
|
|
127
|
+
}
|
|
128
|
+
|
|
122
129
|
@end
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
NS_ASSUME_NONNULL_BEGIN
|
|
4
4
|
|
|
5
|
-
/// Posted when the container of a slot appears or changes its size; the object is the slot id.
|
|
6
|
-
extern NSNotificationName const ANNSlotContainerDidChangeNotification;
|
|
7
|
-
|
|
8
5
|
/// A component named with this prefix is a screen of the app's one React tree: it has no React surface of its own,
|
|
9
6
|
/// and the slot of the tree with its componentId shows it. Events and the state give its name without the prefix.
|
|
10
7
|
extern NSString *const ANNSlotPrefix;
|
|
@@ -17,8 +14,14 @@ NSString *ANNComponentNameWithoutSlotPrefix(NSString *name);
|
|
|
17
14
|
- (void)slotContentDidMount;
|
|
18
15
|
@end
|
|
19
16
|
|
|
20
|
-
///
|
|
21
|
-
|
|
17
|
+
/// A slot view of the app's React tree: it hears when the container of its slot id appears, goes or changes its size.
|
|
18
|
+
@protocol ANNSlotView <NSObject>
|
|
19
|
+
- (void)slotContainerDidChange:(NSString *)slotId;
|
|
20
|
+
@end
|
|
21
|
+
|
|
22
|
+
/// The native containers the slots of the app's React tree show their children in, by slot id, and the slot views by
|
|
23
|
+
/// the same id: a change of a container goes straight to the views of its id. A destroyed container and a deallocated
|
|
24
|
+
/// view drop out on their own. Main queue only.
|
|
22
25
|
@interface ANNSlotContainers : NSObject
|
|
23
26
|
|
|
24
27
|
+ (void)setContainer:(UIView *)container forSlotId:(NSString *)slotId;
|
|
@@ -26,9 +29,14 @@ NSString *ANNComponentNameWithoutSlotPrefix(NSString *name);
|
|
|
26
29
|
/// The container goes: its slot takes its content back.
|
|
27
30
|
+ (void)removeContainerForSlotId:(NSString *)slotId;
|
|
28
31
|
|
|
29
|
-
/// The container of a slot appeared or changed its size.
|
|
32
|
+
/// The container of a slot appeared or changed its size: the slot views under its id, and only they, hear it.
|
|
30
33
|
+ (void)containerDidChange:(NSString *)slotId;
|
|
31
34
|
|
|
35
|
+
/// The slot view goes under its slot id, and out from under the id it had. Held weakly.
|
|
36
|
+
+ (void)registerSlotView:(id<ANNSlotView>)view forSlotId:(NSString *)slotId;
|
|
37
|
+
/// The slot view unmounted or lost its slot id: no change of a container reaches it.
|
|
38
|
+
+ (void)unregisterSlotView:(id<ANNSlotView>)view;
|
|
39
|
+
|
|
32
40
|
@end
|
|
33
41
|
|
|
34
42
|
NS_ASSUME_NONNULL_END
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#import "ANNSlotContainers.h"
|
|
2
|
+
#import "ANNAssert.h"
|
|
2
3
|
|
|
3
|
-
NSNotificationName const ANNSlotContainerDidChangeNotification = @"ANNSlotContainerDidChange";
|
|
4
4
|
NSString *const ANNSlotPrefix = @"slot:";
|
|
5
5
|
|
|
6
6
|
NSString *ANNComponentNameWithoutSlotPrefix(NSString *name) {
|
|
@@ -32,8 +32,81 @@ NSString *ANNComponentNameWithoutSlotPrefix(NSString *name) {
|
|
|
32
32
|
[self containerDidChange:slotId];
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// The slot views by slot id, weakly, in the order they came. Two live views under one id both stay and both hear
|
|
36
|
+
// the change, in that order, as every view with the id did when the change went to all of them.
|
|
37
|
+
+ (NSMutableDictionary<NSString *, NSPointerArray *> *)slotViews {
|
|
38
|
+
static NSMutableDictionary *slotViews;
|
|
39
|
+
static dispatch_once_t once;
|
|
40
|
+
dispatch_once(&once, ^{
|
|
41
|
+
slotViews = [NSMutableDictionary dictionary];
|
|
42
|
+
});
|
|
43
|
+
return slotViews;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The id each slot view is under, so a view is under one id at a time.
|
|
47
|
+
+ (NSMapTable<id<ANNSlotView>, NSString *> *)slotIds {
|
|
48
|
+
static NSMapTable *slotIds;
|
|
49
|
+
static dispatch_once_t once;
|
|
50
|
+
dispatch_once(&once, ^{
|
|
51
|
+
slotIds = [[NSMapTable alloc]
|
|
52
|
+
initWithKeyOptions:NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality
|
|
53
|
+
valueOptions:NSPointerFunctionsStrongMemory
|
|
54
|
+
capacity:0];
|
|
55
|
+
});
|
|
56
|
+
return slotIds;
|
|
57
|
+
}
|
|
58
|
+
|
|
35
59
|
+ (void)containerDidChange:(NSString *)slotId {
|
|
36
|
-
|
|
60
|
+
ANNAssertMainQueue();
|
|
61
|
+
// A copy: a view may register or leave while it hears the change.
|
|
62
|
+
for (id<ANNSlotView> view in [self slotViewsForSlotId:slotId]) {
|
|
63
|
+
[view slotContainerDidChange:slotId];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
+ (void)registerSlotView:(id<ANNSlotView>)view forSlotId:(NSString *)slotId {
|
|
68
|
+
ANNAssertMainQueue();
|
|
69
|
+
if ([[self.slotIds objectForKey:view] isEqualToString:slotId]) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
[self unregisterSlotView:view];
|
|
73
|
+
[self.slotIds setObject:slotId forKey:view];
|
|
74
|
+
NSPointerArray *views = self.slotViews[slotId];
|
|
75
|
+
if (views == nil) {
|
|
76
|
+
views = [NSPointerArray weakObjectsPointerArray];
|
|
77
|
+
self.slotViews[slotId] = views;
|
|
78
|
+
}
|
|
79
|
+
[views addPointer:(__bridge void *)view];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
+ (void)unregisterSlotView:(id<ANNSlotView>)view {
|
|
83
|
+
ANNAssertMainQueue();
|
|
84
|
+
NSString *slotId = [self.slotIds objectForKey:view];
|
|
85
|
+
if (slotId == nil) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
[self.slotIds removeObjectForKey:view];
|
|
89
|
+
[self pruneSlotId:slotId dropping:view];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
+ (NSArray<id<ANNSlotView>> *)slotViewsForSlotId:(NSString *)slotId {
|
|
93
|
+
[self pruneSlotId:slotId dropping:nil];
|
|
94
|
+
return self.slotViews[slotId].allObjects ?: @[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Drops the deallocated views under the id and `view`; an id with no views left goes. By hand rather than with
|
|
98
|
+
// `compact`, which keeps the nil entries of a weak pointer array.
|
|
99
|
+
+ (void)pruneSlotId:(NSString *)slotId dropping:(nullable id<ANNSlotView>)view {
|
|
100
|
+
NSPointerArray *views = self.slotViews[slotId];
|
|
101
|
+
for (NSInteger index = (NSInteger)views.count - 1; index >= 0; index--) {
|
|
102
|
+
void *pointer = [views pointerAtIndex:(NSUInteger)index];
|
|
103
|
+
if (pointer == NULL || pointer == (__bridge void *)view) {
|
|
104
|
+
[views removePointerAtIndex:(NSUInteger)index];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (views != nil && views.count == 0) {
|
|
108
|
+
[self.slotViews removeObjectForKey:slotId];
|
|
109
|
+
}
|
|
37
110
|
}
|
|
38
111
|
|
|
39
112
|
@end
|
|
@@ -17,7 +17,7 @@ typedef NS_ENUM(NSInteger, ANNBarButtonsSide) {
|
|
|
17
17
|
/// merge of a side. Each button is an `ANNUIBarButtonItem` of the first kind its fields give (React view → icon and
|
|
18
18
|
/// text → image button → SF Symbol → image → text → system item); a button without `id` is not placed. Side colors go
|
|
19
19
|
/// onto what is built, never into the options; what the user chose (a toggle, a menu's check) goes into the screen's
|
|
20
|
-
///
|
|
20
|
+
/// user layer. The React buttons' slots are kept by component id, so a button built again reuses its slot. Main queue.
|
|
21
21
|
@interface ANNBarButtons : NSObject
|
|
22
22
|
|
|
23
23
|
- (instancetype)initWithScreen:(UIViewController *)screen
|
|
@@ -49,12 +49,16 @@ typedef NS_ENUM(NSInteger, ANNBarButtonsSide) {
|
|
|
49
49
|
/// The width the slot of a React button measured; 0 before it rendered.
|
|
50
50
|
- (CGFloat)measuredWidthOf:(NSString *)componentId;
|
|
51
51
|
|
|
52
|
-
/// A menu item `itemId` was picked: `ANN.NavigationButtonPressed` with its id, then
|
|
53
|
-
///
|
|
54
|
-
/// the
|
|
52
|
+
/// A menu item `itemId` was picked: `ANN.NavigationButtonPressed` with its id, then, in a single-selection list, the
|
|
53
|
+
/// pick kept in the screen's user layer under `choiceKey`, one choice per menu. The resolution plays the picks again on
|
|
54
|
+
/// a copy of the menu it has. `menuOf` gives the menu in `options` (a button's, the title's, the overflow menu),
|
|
55
|
+
/// `setMenu` puts a changed copy there. A merge into the screen takes a pick away when the screen's resolution after it
|
|
56
|
+
/// has no such item, or when it gives the menu with other checks in the pick's level than the app had there, as on
|
|
57
|
+
/// Android. The two blocks capture no presenter: the node keeps them with the choice.
|
|
55
58
|
- (void)menuItemPicked:(NSString *)itemId
|
|
59
|
+
choiceKey:(NSString *)choiceKey
|
|
56
60
|
menuOf:(ANNMenuOptions *_Nullable (^)(ANNNavigationOptions *options))menuOf
|
|
57
|
-
setMenu:(void (^)(ANNNavigationOptions *
|
|
61
|
+
setMenu:(void (^)(ANNNavigationOptions *options, ANNMenuOptions *menu))setMenu;
|
|
58
62
|
|
|
59
63
|
/// A copy of `menu` that shares no item with it (a new `ANNMenuOptions` and a new `ANNMenuItemOptions` per item, at
|
|
60
64
|
/// any depth).
|