expo-router 7.0.0-canary-20251216-6e1f9a7 → 7.0.0-canary-20251223-b83b31e

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.
Files changed (129) hide show
  1. package/android/build.gradle +2 -2
  2. package/build/color/index.d.ts +44 -0
  3. package/build/color/index.d.ts.map +1 -1
  4. package/build/color/index.js +69 -5
  5. package/build/color/index.js.map +1 -1
  6. package/build/exports.d.ts +1 -0
  7. package/build/exports.d.ts.map +1 -1
  8. package/build/exports.js +3 -1
  9. package/build/exports.js.map +1 -1
  10. package/build/layouts/StackClient.d.ts +3 -3
  11. package/build/layouts/stack-utils/StackHeaderButton.d.ts +77 -2
  12. package/build/layouts/stack-utils/StackHeaderButton.d.ts.map +1 -1
  13. package/build/layouts/stack-utils/StackHeaderButton.js +3 -0
  14. package/build/layouts/stack-utils/StackHeaderButton.js.map +1 -1
  15. package/build/layouts/stack-utils/StackHeaderComponent.d.ts +84 -1
  16. package/build/layouts/stack-utils/StackHeaderComponent.d.ts.map +1 -1
  17. package/build/layouts/stack-utils/StackHeaderComponent.js +54 -1
  18. package/build/layouts/stack-utils/StackHeaderComponent.js.map +1 -1
  19. package/build/layouts/stack-utils/StackHeaderLeftRight.d.ts +107 -3
  20. package/build/layouts/stack-utils/StackHeaderLeftRight.d.ts.map +1 -1
  21. package/build/layouts/stack-utils/StackHeaderLeftRight.js +108 -13
  22. package/build/layouts/stack-utils/StackHeaderLeftRight.js.map +1 -1
  23. package/build/layouts/stack-utils/StackHeaderMenu.d.ts +83 -7
  24. package/build/layouts/stack-utils/StackHeaderMenu.d.ts.map +1 -1
  25. package/build/layouts/stack-utils/StackHeaderMenu.js +14 -4
  26. package/build/layouts/stack-utils/StackHeaderMenu.js.map +1 -1
  27. package/build/layouts/stack-utils/StackHeaderSpacer.d.ts +7 -1
  28. package/build/layouts/stack-utils/StackHeaderSpacer.d.ts.map +1 -1
  29. package/build/layouts/stack-utils/StackHeaderSpacer.js +5 -2
  30. package/build/layouts/stack-utils/StackHeaderSpacer.js.map +1 -1
  31. package/build/layouts/stack-utils/{StackHeaderItem.d.ts → StackHeaderView.d.ts} +18 -5
  32. package/build/layouts/stack-utils/StackHeaderView.d.ts.map +1 -0
  33. package/build/layouts/stack-utils/{StackHeaderItem.js → StackHeaderView.js} +14 -10
  34. package/build/layouts/stack-utils/StackHeaderView.js.map +1 -0
  35. package/build/layouts/stack-utils/index.d.ts +5 -5
  36. package/build/layouts/stack-utils/index.d.ts.map +1 -1
  37. package/build/layouts/stack-utils/index.js +4 -4
  38. package/build/layouts/stack-utils/index.js.map +1 -1
  39. package/build/layouts/stack-utils/shared.d.ts +4 -42
  40. package/build/layouts/stack-utils/shared.d.ts.map +1 -1
  41. package/build/layouts/stack-utils/shared.js +3 -22
  42. package/build/layouts/stack-utils/shared.js.map +1 -1
  43. package/build/link/ExpoLink.d.ts.map +1 -1
  44. package/build/link/ExpoLink.js +1 -8
  45. package/build/link/ExpoLink.js.map +1 -1
  46. package/build/link/elements.d.ts +36 -13
  47. package/build/link/elements.d.ts.map +1 -1
  48. package/build/link/elements.js +14 -5
  49. package/build/link/elements.js.map +1 -1
  50. package/build/link/preview/native.d.ts +12 -1
  51. package/build/link/preview/native.d.ts.map +1 -1
  52. package/build/link/preview/native.js.map +1 -1
  53. package/build/navigationEvents/index.d.ts +36 -0
  54. package/build/navigationEvents/index.d.ts.map +1 -0
  55. package/build/navigationEvents/index.js +53 -0
  56. package/build/navigationEvents/index.js.map +1 -0
  57. package/build/toolbar/elements.d.ts +325 -16
  58. package/build/toolbar/elements.d.ts.map +1 -1
  59. package/build/toolbar/elements.js +130 -12
  60. package/build/toolbar/elements.js.map +1 -1
  61. package/build/toolbar/index.d.ts +38 -6
  62. package/build/toolbar/index.d.ts.map +1 -1
  63. package/build/toolbar/index.js +36 -1
  64. package/build/toolbar/index.js.map +1 -1
  65. package/build/toolbar/native.ios.d.ts.map +1 -1
  66. package/build/toolbar/native.ios.js.map +1 -1
  67. package/build/toolbar/native.types.d.ts +6 -5
  68. package/build/toolbar/native.types.d.ts.map +1 -1
  69. package/build/toolbar/native.types.js.map +1 -1
  70. package/build/useScreens.d.ts.map +1 -1
  71. package/build/useScreens.js +50 -0
  72. package/build/useScreens.js.map +1 -1
  73. package/build/utils/font.d.ts +9 -0
  74. package/build/utils/font.d.ts.map +1 -0
  75. package/build/utils/font.js +20 -0
  76. package/build/utils/font.js.map +1 -0
  77. package/expo-module.config.json +1 -1
  78. package/ios/LinkPreview/LinkPreviewNativeActionView.swift +105 -24
  79. package/ios/LinkPreview/LinkPreviewNativeModule.swift +35 -8
  80. package/ios/LinkPreview/LinkPreviewNativeNavigation.swift +16 -20
  81. package/ios/LinkPreview/LinkPreviewNativePreviewView.swift +1 -8
  82. package/ios/LinkPreview/LinkPreviewNativeView.swift +48 -50
  83. package/ios/LinkPreview/LinkZoomTransition.swift +8 -10
  84. package/ios/RouterViewWithLogger.swift +5 -0
  85. package/ios/Toolbar/RouterFontUtils.swift +50 -0
  86. package/ios/Toolbar/RouterToolbarHostView.swift +41 -17
  87. package/ios/Toolbar/RouterToolbarItemView.swift +30 -13
  88. package/ios/Toolbar/RouterToolbarModule.swift +28 -0
  89. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.aar +0 -0
  90. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.aar.md5 +1 -0
  91. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.aar.sha1 +1 -0
  92. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.aar.sha256 +1 -0
  93. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.aar.sha512 +1 -0
  94. package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.module → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.module} +17 -17
  95. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.module.md5 +1 -0
  96. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.module.sha1 +1 -0
  97. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.module.sha256 +1 -0
  98. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.module.sha512 +1 -0
  99. package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.pom → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.pom} +1 -1
  100. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.pom.md5 +1 -0
  101. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.pom.sha1 +1 -0
  102. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.pom.sha256 +1 -0
  103. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e.pom.sha512 +1 -0
  104. package/local-maven-repo/expo/modules/router/expo.modules.router/maven-metadata.xml +4 -4
  105. package/local-maven-repo/expo/modules/router/expo.modules.router/maven-metadata.xml.md5 +1 -1
  106. package/local-maven-repo/expo/modules/router/expo.modules.router/maven-metadata.xml.sha1 +1 -1
  107. package/local-maven-repo/expo/modules/router/expo.modules.router/maven-metadata.xml.sha256 +1 -1
  108. package/local-maven-repo/expo/modules/router/expo.modules.router/maven-metadata.xml.sha512 +1 -1
  109. package/package.json +10 -10
  110. package/build/layouts/stack-utils/StackHeaderItem.d.ts.map +0 -1
  111. package/build/layouts/stack-utils/StackHeaderItem.js.map +0 -1
  112. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.aar +0 -0
  113. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.aar.md5 +0 -1
  114. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.aar.sha1 +0 -1
  115. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.aar.sha256 +0 -1
  116. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.aar.sha512 +0 -1
  117. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.module.md5 +0 -1
  118. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.module.sha1 +0 -1
  119. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.module.sha256 +0 -1
  120. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.module.sha512 +0 -1
  121. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.pom.md5 +0 -1
  122. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.pom.sha1 +0 -1
  123. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.pom.sha256 +0 -1
  124. package/local-maven-repo/expo/modules/router/expo.modules.router/7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7.pom.sha512 +0 -1
  125. /package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7-sources.jar → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e-sources.jar} +0 -0
  126. /package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7-sources.jar.md5 → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e-sources.jar.md5} +0 -0
  127. /package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7-sources.jar.sha1 → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e-sources.jar.sha1} +0 -0
  128. /package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7-sources.jar.sha256 → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e-sources.jar.sha256} +0 -0
  129. /package/local-maven-repo/expo/modules/router/expo.modules.router/{7.0.0-canary-20251216-6e1f9a7/expo.modules.router-7.0.0-canary-20251216-6e1f9a7-sources.jar.sha512 → 7.0.0-canary-20251223-b83b31e/expo.modules.router-7.0.0-canary-20251223-b83b31e-sources.jar.sha512} +0 -0
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertTextStyleToRNTextStyle = convertTextStyleToRNTextStyle;
4
+ const react_native_1 = require("react-native");
5
+ function convertTextStyleToRNTextStyle(style) {
6
+ const flattenedStyle = react_native_1.StyleSheet.flatten(style);
7
+ if (!flattenedStyle) {
8
+ return undefined;
9
+ }
10
+ if ('fontWeight' in flattenedStyle) {
11
+ return {
12
+ ...flattenedStyle,
13
+ fontWeight: typeof flattenedStyle.fontWeight === 'number'
14
+ ? String(flattenedStyle.fontWeight)
15
+ : flattenedStyle.fontWeight,
16
+ };
17
+ }
18
+ return flattenedStyle;
19
+ }
20
+ //# sourceMappingURL=font.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font.js","sourceRoot":"","sources":["../../src/utils/font.ts"],"names":[],"mappings":";;AAMA,sEAqBC;AA3BD,+CAA0E;AAM1E,SAAgB,6BAA6B,CAC3C,KAA2C;IAM3C,MAAM,cAAc,GAAG,yBAAU,CAAC,OAAO,CAAC,KAAK,CAA8B,CAAC;IAC9E,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,YAAY,IAAI,cAAc,EAAE,CAAC;QACnC,OAAO;YACL,GAAG,cAAc;YACjB,UAAU,EACR,OAAO,cAAc,CAAC,UAAU,KAAK,QAAQ;gBAC3C,CAAC,CAAE,MAAM,CAAC,cAAc,CAAC,UAAU,CAA4B;gBAC/D,CAAC,CAAC,cAAc,CAAC,UAAU;SAChC,CAAC;IACJ,CAAC;IACD,OAAO,cAAmD,CAAC;AAC7D,CAAC","sourcesContent":["import { StyleSheet, type StyleProp, type TextStyle } from 'react-native';\n\ntype NumericFontWeight = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\ntype ConvertedFontWeightType = Exclude<TextStyle['fontWeight'], number> | `${NumericFontWeight}`;\n\nexport function convertTextStyleToRNTextStyle<BaseStyleType extends Pick<TextStyle, 'fontWeight'>>(\n style: StyleProp<BaseStyleType | undefined>\n):\n | (Omit<BaseStyleType, 'fontWeight'> & {\n fontWeight?: ConvertedFontWeightType;\n })\n | undefined {\n const flattenedStyle = StyleSheet.flatten(style) as BaseStyleType | undefined;\n if (!flattenedStyle) {\n return undefined;\n }\n if ('fontWeight' in flattenedStyle) {\n return {\n ...flattenedStyle,\n fontWeight:\n typeof flattenedStyle.fontWeight === 'number'\n ? (String(flattenedStyle.fontWeight) as `${NumericFontWeight}`)\n : flattenedStyle.fontWeight,\n };\n }\n return flattenedStyle as Omit<BaseStyleType, 'fontWeight'>;\n}\n\nexport type BasicTextStyle = Pick<TextStyle, 'fontSize' | 'fontWeight' | 'fontFamily' | 'color'>;\n"]}
@@ -9,7 +9,7 @@
9
9
  "publication": {
10
10
  "groupId": "expo.modules.router",
11
11
  "artifactId": "expo.modules.router",
12
- "version": "7.0.0-canary-20251216-6e1f9a7",
12
+ "version": "7.0.0-canary-20251223-b83b31e",
13
13
  "repository": "local-maven-repo"
14
14
  }
15
15
  }
@@ -1,8 +1,8 @@
1
1
  import ExpoModulesCore
2
- import WebKit
3
2
 
4
- class LinkPreviewNativeActionView: ExpoView, LinkPreviewMenuUpdatable {
3
+ class LinkPreviewNativeActionView: RouterViewWithLogger, LinkPreviewMenuUpdatable {
5
4
  var identifier: String = ""
5
+ // TODO(@ubax): Add @ReactiveProp similar to RouterToolbar to reduce repetition
6
6
  // MARK: - Shared props
7
7
  var title: String = "" {
8
8
  didSet {
@@ -28,13 +28,16 @@ class LinkPreviewNativeActionView: ExpoView, LinkPreviewMenuUpdatable {
28
28
  }
29
29
  }
30
30
  }
31
-
32
- // MARK: - Action only props
33
- var disabled: Bool? {
31
+ var disabled: Bool = false {
34
32
  didSet {
35
33
  updateUiAction()
34
+ if isMenuAction {
35
+ updateMenu()
36
+ }
36
37
  }
37
38
  }
39
+
40
+ // MARK: - Action only props
38
41
  var isOn: Bool? {
39
42
  didSet {
40
43
  updateUiAction()
@@ -45,6 +48,16 @@ class LinkPreviewNativeActionView: ExpoView, LinkPreviewMenuUpdatable {
45
48
  updateUiAction()
46
49
  }
47
50
  }
51
+ var discoverabilityLabel: String? {
52
+ didSet {
53
+ updateUiAction()
54
+ }
55
+ }
56
+ var subtitle: String? {
57
+ didSet {
58
+ updateUiAction()
59
+ }
60
+ }
48
61
 
49
62
  // MARK: - Menu only props
50
63
  var singleSelection: Bool = false {
@@ -68,11 +81,71 @@ class LinkPreviewNativeActionView: ExpoView, LinkPreviewMenuUpdatable {
68
81
  }
69
82
  }
70
83
  }
84
+
85
+ // MARK: - UIBarButtonItem props
86
+ var routerHidden: Bool = false {
87
+ didSet {
88
+ updateUiAction()
89
+ if isMenuAction {
90
+ updateMenu()
91
+ }
92
+ }
93
+ }
94
+ var titleStyle: TitleStyle? {
95
+ didSet {
96
+ if isMenuAction {
97
+ updateMenu()
98
+ }
99
+ }
100
+ }
101
+ var sharesBackground: Bool? {
102
+ didSet {
103
+ if isMenuAction {
104
+ updateMenu()
105
+ }
106
+ }
107
+ }
108
+ var hidesSharedBackground: Bool? {
109
+ didSet {
110
+ if isMenuAction {
111
+ updateMenu()
112
+ }
113
+ }
114
+ }
115
+ var customTintColor: UIColor? {
116
+ didSet {
117
+ updateUiAction()
118
+ if isMenuAction {
119
+ updateMenu()
120
+ }
121
+ }
122
+ }
123
+ var barButtonItemStyle: UIBarButtonItem.Style? {
124
+ didSet {
125
+ if isMenuAction {
126
+ updateMenu()
127
+ }
128
+ }
129
+ }
71
130
  var subActions: [LinkPreviewNativeActionView] = [] {
72
131
  didSet {
73
132
  updateMenu()
74
133
  }
75
134
  }
135
+ var accessibilityLabelForMenu: String? {
136
+ didSet {
137
+ if isMenuAction {
138
+ updateMenu()
139
+ }
140
+ }
141
+ }
142
+ var accessibilityHintForMenu: String? {
143
+ didSet {
144
+ if isMenuAction {
145
+ updateMenu()
146
+ }
147
+ }
148
+ }
76
149
 
77
150
  // MARK: - Events
78
151
  let onSelected = EventDispatcher()
@@ -133,6 +206,9 @@ class LinkPreviewNativeActionView: ExpoView, LinkPreviewMenuUpdatable {
133
206
  var attributes: UIMenuElement.Attributes = []
134
207
  if destructive == true { attributes.insert(.destructive) }
135
208
  if disabled == true { attributes.insert(.disabled) }
209
+ if routerHidden {
210
+ attributes.insert(.hidden)
211
+ }
136
212
 
137
213
  if #available(iOS 16.0, *) {
138
214
  if keepPresented == true { attributes.insert(.keepsMenuPresented) }
@@ -143,31 +219,36 @@ class LinkPreviewNativeActionView: ExpoView, LinkPreviewMenuUpdatable {
143
219
  baseUiAction.attributes = attributes
144
220
  baseUiAction.state = isOn == true ? .on : .off
145
221
 
222
+ if let subtitle = subtitle {
223
+ baseUiAction.subtitle = subtitle
224
+ }
225
+ if let label = discoverabilityLabel {
226
+ baseUiAction.discoverabilityTitle = label
227
+ }
228
+
146
229
  parentMenuUpdatable?.updateMenu()
147
230
  }
148
231
 
149
- #if RCT_NEW_ARCH_ENABLED
150
- override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
151
- if let childActionView = childComponentView as? LinkPreviewNativeActionView {
152
- subActions.insert(childActionView, at: index)
153
- childActionView.parentMenuUpdatable = self
154
- } else {
155
- print(
156
- "ExpoRouter: Unknown child component view (\(childComponentView)) mounted to NativeLinkPreviewActionView"
157
- )
158
- }
232
+ override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
233
+ if let childActionView = childComponentView as? LinkPreviewNativeActionView {
234
+ subActions.insert(childActionView, at: index)
235
+ childActionView.parentMenuUpdatable = self
236
+ } else {
237
+ logger?.warn(
238
+ "[expo-router] Unknown child component view (\(childComponentView)) mounted to NativeLinkPreviewActionView. This is most likely a bug in expo-router."
239
+ )
159
240
  }
241
+ }
160
242
 
161
- override func unmountChildComponentView(_ child: UIView, index: Int) {
162
- if let childActionView = child as? LinkPreviewNativeActionView {
163
- subActions.removeAll(where: { $0 == childActionView })
164
- } else {
165
- print(
166
- "ExpoRouter: Unknown child component view (\(child)) unmounted from NativeLinkPreviewActionView"
167
- )
168
- }
243
+ override func unmountChildComponentView(_ child: UIView, index: Int) {
244
+ if let childActionView = child as? LinkPreviewNativeActionView {
245
+ subActions.removeAll(where: { $0 == childActionView })
246
+ } else {
247
+ logger?.warn(
248
+ "ExpoRouter: Unknown child component view (\(child)) unmounted from NativeLinkPreviewActionView. This is most likely a bug in expo-router."
249
+ )
169
250
  }
170
- #endif
251
+ }
171
252
  }
172
253
 
173
254
  protocol LinkPreviewMenuUpdatable: AnyObject {
@@ -37,7 +37,7 @@ public class LinkPreviewNativeModule: Module {
37
37
  let height = size["height", default: 0]
38
38
 
39
39
  guard width >= 0, height >= 0 else {
40
- print("Preferred content size cannot be negative (\(width), \(height))")
40
+ view.logger?.warn("[expo-router] Preferred content size cannot be negative (\(width), \(height))")
41
41
  return
42
42
  }
43
43
 
@@ -59,16 +59,28 @@ public class LinkPreviewNativeModule: Module {
59
59
  view.icon = icon
60
60
  }
61
61
  Prop("disabled") { (view: LinkPreviewNativeActionView, disabled: Bool?) in
62
- view.disabled = disabled
62
+ view.disabled = disabled ?? false
63
63
  }
64
64
  Prop("destructive") { (view: LinkPreviewNativeActionView, destructive: Bool?) in
65
65
  view.destructive = destructive
66
66
  }
67
- Prop("singleSelection") { (view: LinkPreviewNativeActionView, singleSelection: Bool) in
68
- view.singleSelection = singleSelection
67
+ Prop("discoverabilityLabel") { (view: LinkPreviewNativeActionView, label: String?) in
68
+ view.discoverabilityLabel = label
69
69
  }
70
- Prop("displayAsPalette") { (view: LinkPreviewNativeActionView, displayAsPalette: Bool) in
71
- view.displayAsPalette = displayAsPalette
70
+ Prop("subtitle") { (view: LinkPreviewNativeActionView, subtitle: String?) in
71
+ view.subtitle = subtitle
72
+ }
73
+ Prop("accessibilityLabel") { (view: LinkPreviewNativeActionView, label: String?) in
74
+ view.accessibilityLabelForMenu = label
75
+ }
76
+ Prop("accessibilityHint") { (view: LinkPreviewNativeActionView, hint: String?) in
77
+ view.accessibilityHintForMenu = hint
78
+ }
79
+ Prop("singleSelection") { (view: LinkPreviewNativeActionView, singleSelection: Bool?) in
80
+ view.singleSelection = singleSelection ?? false
81
+ }
82
+ Prop("displayAsPalette") { (view: LinkPreviewNativeActionView, displayAsPalette: Bool?) in
83
+ view.displayAsPalette = displayAsPalette ?? false
72
84
  }
73
85
  Prop("isOn") { (view: LinkPreviewNativeActionView, isOn: Bool?) in
74
86
  view.isOn = isOn
@@ -76,8 +88,23 @@ public class LinkPreviewNativeModule: Module {
76
88
  Prop("keepPresented") { (view: LinkPreviewNativeActionView, keepPresented: Bool?) in
77
89
  view.keepPresented = keepPresented
78
90
  }
79
- Prop("displayInline") { (view: LinkPreviewNativeActionView, displayInline: Bool) in
80
- view.displayInline = displayInline
91
+ Prop("displayInline") { (view: LinkPreviewNativeActionView, displayInline: Bool?) in
92
+ view.displayInline = displayInline ?? false
93
+ }
94
+ Prop("hidden") { (view: LinkPreviewNativeActionView, hidden: Bool?) in
95
+ view.routerHidden = hidden ?? false
96
+ }
97
+ Prop("sharesBackground") { (view: LinkPreviewNativeActionView, sharesBackground: Bool?) in
98
+ view.sharesBackground = sharesBackground
99
+ }
100
+ Prop("hidesSharedBackground") { (view: LinkPreviewNativeActionView, hidesSharedBackground: Bool?) in
101
+ view.hidesSharedBackground = hidesSharedBackground
102
+ }
103
+ Prop("tintColor") { (view: LinkPreviewNativeActionView, tintColor: UIColor?) in
104
+ view.customTintColor = tintColor
105
+ }
106
+ Prop("barButtonItemStyle") { (view: LinkPreviewNativeActionView, style: BarItemStyle?) in
107
+ view.barButtonItemStyle = style?.toUIBarButtonItemStyle()
81
108
  }
82
109
 
83
110
  Events("onSelected")
@@ -8,8 +8,8 @@ struct TabChangeCommand {
8
8
  }
9
9
 
10
10
  internal class LinkPreviewNativeNavigation {
11
- private var preloadedScreenView: RNSScreenView?
12
- private var preloadedStackView: RNSScreenStackView?
11
+ private weak var preloadedScreenView: RNSScreenView?
12
+ private weak var preloadedStackView: RNSScreenStackView?
13
13
  private var tabChangeCommands: [TabChangeCommand] = []
14
14
  private let logger: Logger?
15
15
 
@@ -27,7 +27,7 @@ internal class LinkPreviewNativeNavigation {
27
27
  // If there were, the preview transition could be to a different tab only
28
28
  if self.tabChangeCommands.isEmpty {
29
29
  logger?.warn(
30
- "expo-router: No preloaded screen view to push. You should only use Link.Preview when navigating inside a stack or native tabs navigator."
30
+ "[expo-router] No preloaded screen view to push. Link.Preview transition is only supported inside a native stack or native tabs navigators."
31
31
  )
32
32
  }
33
33
  return
@@ -45,22 +45,19 @@ internal class LinkPreviewNativeNavigation {
45
45
  let oldTabKeys = tabPath?.path.map { $0.oldTabKey } ?? []
46
46
  let stackOrTabView = findStackViewWithScreenIdOrTabBarController(
47
47
  screenId: screenId, tabKeys: oldTabKeys, responder: responder)
48
- if stackOrTabView != nil {
49
- if let tabView = stackOrTabView as? RNSBottomTabsScreenComponentView {
50
- let newTabKeys = tabPath?.path.map { $0.newTabKey } ?? []
51
- // The order is important here. findStackViewWithScreenIdInSubViews must be called
52
- // even if screenId is nil to compute the tabChangeCommands.
53
- if let stackView = findStackViewWithScreenIdInSubViews(
54
- screenId: screenId, tabKeys: newTabKeys, rootView: tabView), let screenId {
55
- setPreloadedView(stackView: stackView, screenId: screenId)
56
- }
57
- } else if let stackView = stackOrTabView as? RNSScreenStackView, let screenId {
48
+ guard let stackOrTabView else {
49
+ return
50
+ }
51
+ if let tabView = stackOrTabView as? RNSBottomTabsScreenComponentView {
52
+ let newTabKeys = tabPath?.path.map { $0.newTabKey } ?? []
53
+ // The order is important here. findStackViewWithScreenIdInSubViews must be called
54
+ // even if screenId is nil to compute the tabChangeCommands.
55
+ if let stackView = findStackViewWithScreenIdInSubViews(
56
+ screenId: screenId, tabKeys: newTabKeys, rootView: tabView), let screenId {
58
57
  setPreloadedView(stackView: stackView, screenId: screenId)
59
58
  }
60
- } else {
61
- logger?.warn(
62
- "expo-router: No view found for link preview navigation. You should only use Link.Preview when navigating inside a stack or native tabs navigator."
63
- )
59
+ } else if let stackView = stackOrTabView as? RNSScreenStackView, let screenId {
60
+ setPreloadedView(stackView: stackView, screenId: screenId)
64
61
  }
65
62
  }
66
63
 
@@ -88,8 +85,6 @@ internal class LinkPreviewNativeNavigation {
88
85
  // The first and only child of the inner screen stack should be
89
86
  // RNSScreenView (<ScreenStackItem>).
90
87
  let screenContentView = innerScreenStack.reactSubviews().first as? RNSScreenView {
91
- print("screenView screenId:", screenView.screenId)
92
- print("innerScreenStack screenIds:", innerScreenStack.screenIds)
93
88
  // Same as above, we let React Native Screens handle the transition.
94
89
  // We need to set the activity of inner screen as well, because its
95
90
  // react value is the same as the preloaded screen - 0.
@@ -152,7 +147,8 @@ internal class LinkPreviewNativeNavigation {
152
147
  ) -> (tabIndex: Int, tabView: UIView)? {
153
148
  let views = tabBarController.viewControllers?.compactMap { $0.view } ?? []
154
149
  let enumeratedViews = views.enumerated()
155
- if let result = enumeratedViews
150
+ if let result =
151
+ enumeratedViews
156
152
  .first(where: { _, view in
157
153
  guard let tabView = view as? RNSBottomTabsScreenComponentView, let tabKey = tabView.tabKey
158
154
  else {
@@ -1,16 +1,9 @@
1
1
  import ExpoModulesCore
2
- import WebKit
3
2
 
4
- class NativeLinkPreviewContentView: ExpoView {
3
+ class NativeLinkPreviewContentView: RouterViewWithLogger {
5
4
  var preferredContentSize: CGSize = .zero
6
5
 
7
- required init(appContext: AppContext? = nil) {
8
- super.init(appContext: appContext)
9
- }
10
-
11
6
  func setInitialSize(bounds: CGRect) {
12
- #if RCT_NEW_ARCH_ENABLED
13
7
  self.setShadowNodeSize(Float(bounds.width), height: Float(bounds.height))
14
- #endif
15
8
  }
16
9
  }
@@ -1,7 +1,7 @@
1
1
  import ExpoModulesCore
2
2
  import RNScreens
3
3
 
4
- class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate,
4
+ class NativeLinkPreviewView: RouterViewWithLogger, UIContextMenuInteractionDelegate,
5
5
  RNSDismissibleModalProtocol, LinkPreviewMenuUpdatable {
6
6
  private var preview: NativeLinkPreviewContentView?
7
7
  private var interaction: UIContextMenuInteraction?
@@ -19,7 +19,7 @@ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate,
19
19
  private var actions: [LinkPreviewNativeActionView] = []
20
20
 
21
21
  private lazy var linkPreviewNativeNavigation: LinkPreviewNativeNavigation = {
22
- return LinkPreviewNativeNavigation(logger: appContext?.jsLogger)
22
+ return LinkPreviewNativeNavigation(logger: logger)
23
23
  }()
24
24
 
25
25
  let onPreviewTapped = EventDispatcher()
@@ -53,64 +53,62 @@ class NativeLinkPreviewView: ExpoView, UIContextMenuInteractionDelegate,
53
53
  }
54
54
 
55
55
  // MARK: - Children
56
- #if RCT_NEW_ARCH_ENABLED
57
- override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
58
- if let previewView = childComponentView as? NativeLinkPreviewContentView {
59
- preview = previewView
60
- } else if let actionView = childComponentView as? LinkPreviewNativeActionView {
61
- actionView.parentMenuUpdatable = self
62
- actions.append(actionView)
63
- } else {
64
- if directChild != nil {
65
- print(
66
- "[expo-router] Found a second child of <Link.Trigger>. Only one is allowed. This is most likely a bug in expo-router."
56
+ override func mountChildComponentView(_ childComponentView: UIView, index: Int) {
57
+ if let previewView = childComponentView as? NativeLinkPreviewContentView {
58
+ preview = previewView
59
+ } else if let actionView = childComponentView as? LinkPreviewNativeActionView {
60
+ actionView.parentMenuUpdatable = self
61
+ actions.append(actionView)
62
+ } else {
63
+ if directChild != nil {
64
+ logger?.warn(
65
+ "[expo-router] Found a second child of <Link.Trigger>. Only one is allowed. This is most likely a bug in expo-router."
66
+ )
67
+ return
68
+ }
69
+ directChild = childComponentView
70
+ if let interaction = self.interaction {
71
+ if let indirectTrigger = childComponentView as? LinkPreviewIndirectTriggerProtocol {
72
+ indirectTrigger.indirectTrigger?.addInteraction(interaction)
73
+ } else {
74
+ childComponentView.addInteraction(interaction)
75
+ }
76
+ }
77
+ super.mountChildComponentView(childComponentView, index: index)
78
+ }
79
+ }
80
+
81
+ override func unmountChildComponentView(_ child: UIView, index: Int) {
82
+ if child is NativeLinkPreviewContentView {
83
+ preview = nil
84
+ } else if let actionView = child as? LinkPreviewNativeActionView {
85
+ actions.removeAll(where: {
86
+ $0 == actionView
87
+ })
88
+ } else {
89
+ if let directChild = directChild {
90
+ if directChild != child {
91
+ logger?.warn(
92
+ "[expo-router] Unmounting unexpected child from <Link.Trigger>. This is most likely a bug in expo-router."
67
93
  )
68
94
  return
69
95
  }
70
- directChild = childComponentView
71
96
  if let interaction = self.interaction {
72
- if let indirectTrigger = childComponentView as? LinkPreviewIndirectTriggerProtocol {
73
- indirectTrigger.indirectTrigger?.addInteraction(interaction)
97
+ if let indirectTrigger = directChild as? LinkPreviewIndirectTriggerProtocol {
98
+ indirectTrigger.indirectTrigger?.removeInteraction(interaction)
74
99
  } else {
75
- childComponentView.addInteraction(interaction)
100
+ directChild.removeInteraction(interaction)
76
101
  }
77
102
  }
78
- super.mountChildComponentView(childComponentView, index: index)
79
- }
80
- }
81
-
82
- override func unmountChildComponentView(_ child: UIView, index: Int) {
83
- if child is NativeLinkPreviewContentView {
84
- preview = nil
85
- } else if let actionView = child as? LinkPreviewNativeActionView {
86
- actions.removeAll(where: {
87
- $0 == actionView
88
- })
103
+ super.unmountChildComponentView(child, index: index)
89
104
  } else {
90
- if let directChild = directChild {
91
- if directChild != child {
92
- print(
93
- "[expo-router] Unmounting unexpected child from <Link.Trigger>. This is most likely a bug in expo-router."
94
- )
95
- return
96
- }
97
- if let interaction = self.interaction {
98
- if let indirectTrigger = directChild as? LinkPreviewIndirectTriggerProtocol {
99
- indirectTrigger.indirectTrigger?.removeInteraction(interaction)
100
- } else {
101
- directChild.removeInteraction(interaction)
102
- }
103
- }
104
- super.unmountChildComponentView(child, index: index)
105
- } else {
106
- print(
107
- "[expo-router] No link child found to unmount. This is most likely a bug in expo-router."
108
- )
109
- return
110
- }
105
+ logger?.warn(
106
+ "[expo-router] No link child found to unmount. This is most likely a bug in expo-router."
107
+ )
108
+ return
111
109
  }
112
110
  }
113
- #endif
111
+ }
114
112
 
115
113
  // MARK: - UIContextMenuInteractionDelegate
116
114
 
@@ -244,7 +244,7 @@ class LinkZoomTransitionAlignmentRectDetector: LinkZoomExpoView {
244
244
  ) {
245
245
  guard child == nil else {
246
246
  logger?.warn(
247
- "[expo-router] Link.AppleZoomTarget can only have a single native child."
247
+ "[expo-router] Link.AppleZoomTarget can only have a single native child. If you passed a single child, consider adding collapsible={false} to your component"
248
248
  )
249
249
  return
250
250
  }
@@ -287,7 +287,7 @@ class LinkZoomTransitionEnabler: LinkZoomExpoView {
287
287
 
288
288
  private func setupZoomTransition() {
289
289
  if self.zoomTransitionSourceIdentifier.isEmpty {
290
- print("[expo-router] No zoomTransitionSourceIdentifier passed to LinkZoomTransitionEnabler")
290
+ logger?.warn("[expo-router] No zoomTransitionSourceIdentifier passed to LinkZoomTransitionEnabler. This is most likely a bug in expo-router.")
291
291
  return
292
292
  }
293
293
  if let controller = self.findViewController() {
@@ -329,8 +329,8 @@ class LinkZoomTransitionEnabler: LinkZoomExpoView {
329
329
  view = linkPreviewView.directChild
330
330
  }
331
331
  guard let view else {
332
- print(
333
- "[expo-router] No source view found for identifier \(self.zoomTransitionSourceIdentifier) to enable zoom transition"
332
+ self.logger?.warn(
333
+ "[expo-router] No source view found for identifier \(self.zoomTransitionSourceIdentifier) to enable zoom transition. This is most likely a bug in expo-router."
334
334
  )
335
335
  return nil
336
336
  }
@@ -339,7 +339,7 @@ class LinkZoomTransitionEnabler: LinkZoomExpoView {
339
339
  return
340
340
  }
341
341
  } else {
342
- print("[expo-router] No navigation controller found to enable zoom transition")
342
+ logger?.warn("[expo-router] No navigation controller found to enable zoom transition. This is most likely a bug in expo-router.")
343
343
  }
344
344
  }
345
345
 
@@ -390,7 +390,7 @@ class LinkZoomTransitionEnabler: LinkZoomExpoView {
390
390
  }
391
391
  }
392
392
 
393
- class LinkZoomExpoView: ExpoView {
393
+ class LinkZoomExpoView: RouterViewWithLogger {
394
394
  var module: LinkPreviewNativeModule? {
395
395
  return appContext?.moduleRegistry.get(moduleWithName: LinkPreviewNativeModule.moduleName)
396
396
  as? LinkPreviewNativeModule
@@ -398,7 +398,7 @@ class LinkZoomExpoView: ExpoView {
398
398
 
399
399
  var sourceRepository: LinkZoomTransitionsSourceRepository? {
400
400
  guard let module else {
401
- print("[expo-router] LinkPreviewNativeModule not loaded")
401
+ logger?.warn("[expo-router] LinkPreviewNativeModule not loaded. Make sure expo-router is properly configured.")
402
402
  return nil
403
403
  }
404
404
  return module.zoomSourceRepository
@@ -406,11 +406,9 @@ class LinkZoomExpoView: ExpoView {
406
406
 
407
407
  var alignmentViewRepository: LinkZoomTransitionsAlignmentViewRepository? {
408
408
  guard let module else {
409
- print("[expo-router] LinkPreviewNativeModule not loaded")
409
+ logger?.warn("[expo-router] LinkPreviewNativeModule not loaded. Make sure expo-router is properly configured.")
410
410
  return nil
411
411
  }
412
412
  return module.zoomAlignmentViewRepository
413
413
  }
414
-
415
- lazy var logger = appContext?.jsLogger
416
414
  }
@@ -0,0 +1,5 @@
1
+ import ExpoModulesCore
2
+
3
+ class RouterViewWithLogger: ExpoView {
4
+ lazy var logger = appContext?.jsLogger
5
+ }
@@ -0,0 +1,50 @@
1
+ import React
2
+ import UIKit
3
+
4
+ struct RouterFontUtils {
5
+ static func convertTitleStyleToFont(_ titleStyle: TitleStyle) -> UIFont {
6
+ let fontFamily = titleStyle.fontFamily
7
+ let fontWeight = titleStyle.fontWeight
8
+
9
+ let resolvedFontSize = resolveFontSize(titleStyle.fontSize)
10
+
11
+ if fontFamily != nil || fontWeight != nil {
12
+ return RCTFont.update(
13
+ nil,
14
+ withFamily: fontFamily,
15
+ size: NSNumber(value: Float(resolvedFontSize)),
16
+ weight: fontWeight,
17
+ style: nil,
18
+ variant: nil,
19
+ scaleMultiplier: 1.0)
20
+ }
21
+ return UIFont.systemFont(ofSize: resolvedFontSize)
22
+ }
23
+
24
+ static func setTitleStyle(fromConfig titleStyle: TitleStyle, for item: UIBarButtonItem) {
25
+ var attrs: [NSAttributedString.Key: Any] = [:]
26
+
27
+ attrs[.font] = convertTitleStyleToFont(titleStyle)
28
+
29
+ if let color = titleStyle.color {
30
+ attrs[.foregroundColor] = color
31
+ }
32
+
33
+ item.setTitleTextAttributes(attrs, for: .normal)
34
+ item.setTitleTextAttributes(attrs, for: .highlighted)
35
+ item.setTitleTextAttributes(attrs, for: .disabled)
36
+ item.setTitleTextAttributes(attrs, for: .selected)
37
+ item.setTitleTextAttributes(attrs, for: .focused)
38
+ }
39
+
40
+ private static func resolveFontSize(_ fontSize: Double?) -> CGFloat {
41
+ if let fontSize = fontSize {
42
+ return CGFloat(fontSize)
43
+ }
44
+ #if os(tvOS)
45
+ return 17.0
46
+ #else
47
+ return UIFont.labelFontSize
48
+ #endif
49
+ }
50
+ }