@expo/ui 57.0.7 → 57.0.9

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 (83) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/DatePickerView.kt +28 -0
  4. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +5 -1
  5. package/android/src/main/java/expo/modules/ui/RNHostView.kt +36 -28
  6. package/build/community/bottom-sheet/BottomSheet.ios.d.ts.map +1 -1
  7. package/build/community/menu/MenuView.android.d.ts.map +1 -1
  8. package/build/community/menu/MenuView.ios.d.ts.map +1 -1
  9. package/build/community/menu/types.d.ts +6 -1
  10. package/build/community/menu/types.d.ts.map +1 -1
  11. package/build/jetpack-compose/RNHostView/index.d.ts +1 -3
  12. package/build/jetpack-compose/RNHostView/index.d.ts.map +1 -1
  13. package/build/swift-ui/BottomSheet/index.d.ts +10 -6
  14. package/build/swift-ui/BottomSheet/index.d.ts.map +1 -1
  15. package/build/swift-ui/modifiers/index.d.ts +1 -0
  16. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  17. package/build/swift-ui/modifiers/menuOrder.d.ts +10 -0
  18. package/build/swift-ui/modifiers/menuOrder.d.ts.map +1 -0
  19. package/build/swift-ui/modifiers/presentationModifiers.d.ts +19 -0
  20. package/build/swift-ui/modifiers/presentationModifiers.d.ts.map +1 -1
  21. package/expo-module.config.json +1 -1
  22. package/ios/BottomSheetView.swift +20 -3
  23. package/ios/GlassEffectContainerView.swift +1 -1
  24. package/ios/HStackView.swift +1 -1
  25. package/ios/Modifiers/MenuOrderModifier.swift +34 -0
  26. package/ios/Modifiers/PresentationModifiers.swift +30 -0
  27. package/ios/Modifiers/ViewModifierRegistry.swift +8 -0
  28. package/ios/TextFieldView.swift +8 -10
  29. package/ios/VStackView.swift +1 -1
  30. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.7/expo.modules.ui-57.0.7-sources.jar → 57.0.9/expo.modules.ui-57.0.9-sources.jar} +0 -0
  31. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9-sources.jar.md5 +1 -0
  32. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9-sources.jar.sha1 +1 -0
  33. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9-sources.jar.sha256 +1 -0
  34. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9-sources.jar.sha512 +1 -0
  35. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.aar +0 -0
  36. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.aar.md5 +1 -0
  37. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.aar.sha1 +1 -0
  38. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.aar.sha256 +1 -0
  39. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.aar.sha512 +1 -0
  40. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.7/expo.modules.ui-57.0.7.module → 57.0.9/expo.modules.ui-57.0.9.module} +22 -22
  41. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.module.md5 +1 -0
  42. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.module.sha1 +1 -0
  43. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.module.sha256 +1 -0
  44. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.module.sha512 +1 -0
  45. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.7/expo.modules.ui-57.0.7.pom → 57.0.9/expo.modules.ui-57.0.9.pom} +1 -1
  46. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.pom.md5 +1 -0
  47. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.pom.sha1 +1 -0
  48. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.pom.sha256 +1 -0
  49. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.9/expo.modules.ui-57.0.9.pom.sha512 +1 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  55. package/package.json +5 -5
  56. package/src/community/bottom-sheet/BottomSheet.ios.tsx +14 -20
  57. package/src/community/masked-view/MaskedView.ios.tsx +1 -1
  58. package/src/community/menu/MenuView.android.tsx +2 -1
  59. package/src/community/menu/MenuView.ios.tsx +11 -13
  60. package/src/community/menu/types.tsx +12 -1
  61. package/src/jetpack-compose/RNHostView/index.tsx +7 -4
  62. package/src/swift-ui/BottomSheet/index.tsx +21 -14
  63. package/src/swift-ui/modifiers/index.ts +1 -0
  64. package/src/swift-ui/modifiers/menuOrder.ts +12 -0
  65. package/src/swift-ui/modifiers/presentationModifiers.ts +22 -0
  66. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7-sources.jar.md5 +0 -1
  67. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7-sources.jar.sha1 +0 -1
  68. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7-sources.jar.sha256 +0 -1
  69. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7-sources.jar.sha512 +0 -1
  70. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.aar +0 -0
  71. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.aar.md5 +0 -1
  72. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.aar.sha1 +0 -1
  73. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.aar.sha256 +0 -1
  74. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.aar.sha512 +0 -1
  75. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.module.md5 +0 -1
  76. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.module.sha1 +0 -1
  77. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.module.sha256 +0 -1
  78. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.module.sha512 +0 -1
  79. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.pom.md5 +0 -1
  80. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.pom.sha1 +0 -1
  81. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.pom.sha256 +0 -1
  82. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.7/expo.modules.ui-57.0.7.pom.sha512 +0 -1
  83. package/plugin/tsconfig.tsbuildinfo +0 -1
@@ -217,4 +217,34 @@ internal struct PresentationBackgroundModifier: ViewModifier, Record {
217
217
  content
218
218
  }
219
219
  }
220
+ }
221
+
222
+ // MARK: - Presentation Sizing
223
+
224
+ internal enum PresentationSizingOption: String, Enumerable {
225
+ case automatic
226
+ case fitted
227
+ case form
228
+ case page
229
+ }
230
+
231
+ internal struct PresentationSizingModifier: ViewModifier, Record {
232
+ @Field var sizing: PresentationSizingOption = .automatic
233
+
234
+ func body(content: Content) -> some View {
235
+ if #available(iOS 18.0, tvOS 18.0, macOS 15.0, *) {
236
+ switch sizing {
237
+ case .automatic:
238
+ content.presentationSizing(.automatic)
239
+ case .fitted:
240
+ content.presentationSizing(.fitted)
241
+ case .form:
242
+ content.presentationSizing(.form)
243
+ case .page:
244
+ content.presentationSizing(.page)
245
+ }
246
+ } else {
247
+ content
248
+ }
249
+ }
220
250
  }
@@ -2068,6 +2068,10 @@ extension ViewModifierRegistry {
2068
2068
  return try PickerStyleModifier(from: params, appContext: appContext)
2069
2069
  }
2070
2070
 
2071
+ register("menuOrder") { params, appContext, _ in
2072
+ return try MenuOrderModifier(from: params, appContext: appContext)
2073
+ }
2074
+
2071
2075
  register("submitLabel") { params, appContext, _ in
2072
2076
  return try SubmitLabelModifier(from: params, appContext: appContext)
2073
2077
  }
@@ -2136,6 +2140,10 @@ extension ViewModifierRegistry {
2136
2140
  return try PresentationBackgroundModifier(from: params, appContext: appContext)
2137
2141
  }
2138
2142
 
2143
+ register("presentationSizing") { params, appContext, _ in
2144
+ return try PresentationSizingModifier(from: params, appContext: appContext)
2145
+ }
2146
+
2139
2147
  register("listStyle") { params, appContext, _ in
2140
2148
  return try ListStyleModifier(from: params, appContext: appContext)
2141
2149
  }
@@ -219,16 +219,10 @@ private struct StatefulSelectableTextField: View {
219
219
  )
220
220
  }
221
221
 
222
- private var selectionBinding: Binding<SwiftUI.TextSelection?> {
223
- if #available(iOS 26.0, macOS 26.0, tvOS 26.0, *) {
224
- return $localSelection
225
- }
226
- return clampedSelectionBinding
227
- }
228
-
229
- // iOS 18 SwiftUI can throw an out-of-bounds exception when selection becomes invalid after text is changed programmatically.
222
+ // SwiftUI can throw an out-of-bounds exception when selection becomes invalid after text is changed programmatically.
230
223
  // Resetting to nil avoids passing SwiftUI the stale selection.
231
224
  // https://github.com/expo/expo/issues/47434
225
+ // https://github.com/expo/expo/issues/48274
232
226
  // https://github.com/swiftlang/swift/issues/82359#issuecomment-3038538035
233
227
  private var clampedSelectionBinding: Binding<SwiftUI.TextSelection?> {
234
228
  Binding(
@@ -251,7 +245,7 @@ private struct StatefulSelectableTextField: View {
251
245
  TextField(
252
246
  promptText == nil ? props.placeholder : "",
253
247
  text: textBinding,
254
- selection: selectionBinding,
248
+ selection: clampedSelectionBinding,
255
249
  prompt: promptText,
256
250
  axis: swiftUIAxis
257
251
  )
@@ -278,7 +272,11 @@ private struct StatefulSelectableTextField: View {
278
272
  props.onSelectionChange(["start": start, "end": end])
279
273
  }
280
274
  .onChange(of: state.value as? String) { newValue in
281
- guard userMutatingState else { return }
275
+ guard userMutatingState else {
276
+ // The text was replaced from JS; drop the selection so indices built for the old string are never validated against the new one.
277
+ localSelection = nil
278
+ return
279
+ }
282
280
  if let max = props.maxLength, let str = newValue, str.count > max {
283
281
  state.value = String(str.prefix(max))
284
282
  return
@@ -35,7 +35,7 @@ public struct VStackView: ExpoSwiftUI.View {
35
35
  public var body: some View {
36
36
  VStack(
37
37
  alignment: props.alignment?.toHorizontalAlignment() ?? .center,
38
- spacing: CGFloat(props.spacing ?? 0)) {
38
+ spacing: props.spacing.map { CGFloat($0) }) {
39
39
  Children()
40
40
  }
41
41
  }
@@ -0,0 +1 @@
1
+ ab9529856e2d9014c4500bd026ef87271d46390e
@@ -0,0 +1 @@
1
+ daa8952f564fc49bf1e8248beaa2f2bc5f305c02f01f9ea9f9209323034d7cdf
@@ -0,0 +1 @@
1
+ 6773d96e9e4bc26584cb012c44b9e9f6afd9d4d099aa7c93eb35a2089a77b71155a671302db4a9f9d97267c310ad266476d2bbb04ce77cc4fa36f7b3e262e696
@@ -0,0 +1 @@
1
+ 128612487df27ee99651882fde63af29121cec37
@@ -0,0 +1 @@
1
+ 497fae34b972934a9eb0ae4b1343527eb1e5f872ad97c44de758d4e3095cf2d9
@@ -0,0 +1 @@
1
+ 65dd5f448fa7a5db1112becf801262b96f1ded388023a72616e1623ed9eb3fa1aaa25e479db58ee56aed07e97a1e5d72c3b66c4eecd6c8de39f67f92c1dbf993
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "expo.modules.ui",
5
5
  "module": "expo.modules.ui",
6
- "version": "57.0.7",
6
+ "version": "57.0.9",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.ui-57.0.7.aar",
28
- "url": "expo.modules.ui-57.0.7.aar",
29
- "size": 1945789,
30
- "sha512": "1866cb015b14c19bf62eae828e70031e1c64ea23c3264a931f3d1126c87c40d000f4f1d850a00dc4178ba859a930808e99af0ccb9732063bd782f93c400d2865",
31
- "sha256": "1961d3901f622d2a724f44c75361561cde6ab74c25c29e0990467f06d1666ade",
32
- "sha1": "1946982601650cce80a1a4dd0d8969f92ec323c6",
33
- "md5": "a7109c0dee1ee28f19f905bd7deee4e1"
27
+ "name": "expo.modules.ui-57.0.9.aar",
28
+ "url": "expo.modules.ui-57.0.9.aar",
29
+ "size": 1947071,
30
+ "sha512": "65dd5f448fa7a5db1112becf801262b96f1ded388023a72616e1623ed9eb3fa1aaa25e479db58ee56aed07e97a1e5d72c3b66c4eecd6c8de39f67f92c1dbf993",
31
+ "sha256": "497fae34b972934a9eb0ae4b1343527eb1e5f872ad97c44de758d4e3095cf2d9",
32
+ "sha1": "128612487df27ee99651882fde63af29121cec37",
33
+ "md5": "b64c5776b5d32cb3e45c463040737d7a"
34
34
  }
35
35
  ]
36
36
  },
@@ -141,13 +141,13 @@
141
141
  ],
142
142
  "files": [
143
143
  {
144
- "name": "expo.modules.ui-57.0.7.aar",
145
- "url": "expo.modules.ui-57.0.7.aar",
146
- "size": 1945789,
147
- "sha512": "1866cb015b14c19bf62eae828e70031e1c64ea23c3264a931f3d1126c87c40d000f4f1d850a00dc4178ba859a930808e99af0ccb9732063bd782f93c400d2865",
148
- "sha256": "1961d3901f622d2a724f44c75361561cde6ab74c25c29e0990467f06d1666ade",
149
- "sha1": "1946982601650cce80a1a4dd0d8969f92ec323c6",
150
- "md5": "a7109c0dee1ee28f19f905bd7deee4e1"
144
+ "name": "expo.modules.ui-57.0.9.aar",
145
+ "url": "expo.modules.ui-57.0.9.aar",
146
+ "size": 1947071,
147
+ "sha512": "65dd5f448fa7a5db1112becf801262b96f1ded388023a72616e1623ed9eb3fa1aaa25e479db58ee56aed07e97a1e5d72c3b66c4eecd6c8de39f67f92c1dbf993",
148
+ "sha256": "497fae34b972934a9eb0ae4b1343527eb1e5f872ad97c44de758d4e3095cf2d9",
149
+ "sha1": "128612487df27ee99651882fde63af29121cec37",
150
+ "md5": "b64c5776b5d32cb3e45c463040737d7a"
151
151
  }
152
152
  ]
153
153
  },
@@ -161,13 +161,13 @@
161
161
  },
162
162
  "files": [
163
163
  {
164
- "name": "expo.modules.ui-57.0.7-sources.jar",
165
- "url": "expo.modules.ui-57.0.7-sources.jar",
166
- "size": 94421,
167
- "sha512": "23a6ec3e0acaeaa4884440b4410188c75f0e11da49493e08d8bb58a12548db951018417820aa073be5f9aa62000db96be2fe3c04fd88bb073f81f2f964dc145a",
168
- "sha256": "454872b768b077cea96607a41337f2c696ab2a2a71435cc3836e7f2891910d73",
169
- "sha1": "27592d060013020ce468af28cef07d3b45ec26a1",
170
- "md5": "15997973c79a34aad0aa9284381987fa"
164
+ "name": "expo.modules.ui-57.0.9-sources.jar",
165
+ "url": "expo.modules.ui-57.0.9-sources.jar",
166
+ "size": 95127,
167
+ "sha512": "6773d96e9e4bc26584cb012c44b9e9f6afd9d4d099aa7c93eb35a2089a77b71155a671302db4a9f9d97267c310ad266476d2bbb04ce77cc4fa36f7b3e262e696",
168
+ "sha256": "daa8952f564fc49bf1e8248beaa2f2bc5f305c02f01f9ea9f9209323034d7cdf",
169
+ "sha1": "ab9529856e2d9014c4500bd026ef87271d46390e",
170
+ "md5": "861e2a9fc90c0f77fb3e0c4b1e733bda"
171
171
  }
172
172
  ]
173
173
  }
@@ -0,0 +1 @@
1
+ 0d62287cdeb06cde748d14202078e052f144bf26
@@ -0,0 +1 @@
1
+ def27a191bae2bc92b3b80add3200b7b3cf6105abdd64cf3ada19a79c654d443
@@ -0,0 +1 @@
1
+ 1f093ca15f49749b76c317215354083b91a32bdc76f2df531fcda80016b998a06284e37a833d9a6a3e9216079754cbf81af58a1d771da5820d07ba59d38b940f
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>expo.modules.ui</groupId>
11
11
  <artifactId>expo.modules.ui</artifactId>
12
- <version>57.0.7</version>
12
+ <version>57.0.9</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.ui</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ 8ffc8fc85190d30a3c43ab8469335390e884d687
@@ -0,0 +1 @@
1
+ abd14782afd8905e36b66aa0163daa4e8fbc87edf6713eb746b410f0d9f9540a
@@ -0,0 +1 @@
1
+ d269a08d66be52e9c0298ad26600b12583c511bee298490ad7b6c463d111d17c741ff05b0d7824edbd5d68b96a8141be15e28098fbcdf6c05af350ef556dc2b2
@@ -3,11 +3,11 @@
3
3
  <groupId>expo.modules.ui</groupId>
4
4
  <artifactId>expo.modules.ui</artifactId>
5
5
  <versioning>
6
- <latest>57.0.7</latest>
7
- <release>57.0.7</release>
6
+ <latest>57.0.9</latest>
7
+ <release>57.0.9</release>
8
8
  <versions>
9
- <version>57.0.7</version>
9
+ <version>57.0.9</version>
10
10
  </versions>
11
- <lastUpdated>20260717104733</lastUpdated>
11
+ <lastUpdated>20260804071901</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 503392f9eddc198a230bb9807eeba3ec
1
+ 989b98b3b90d23685ff41bb33f723781
@@ -1 +1 @@
1
- 5f6f1fef0b6a516892d1184a839b21e64161b7b4
1
+ b08887ade2f4132d8f01cbede52519862b04646d
@@ -1 +1 @@
1
- 6e8174643669155b28b3d0fd2efdbfe2a1a39afc17bf0c664cdc4900ff59d4fe
1
+ d1fa6df85b93d62f797f175f58f23bedd98fef22784f9f4e876767303e6273a3
@@ -1 +1 @@
1
- e04eacbc3de0906238ddf11eefffd3a5ee1ef0549f3ac6dfaf26c27fdd72cf2238b2a4227f1642ed376bc5a25beaa9b0f0dd497826eb876ca7a9bbbf6a03903e
1
+ c6f0d9c6ee0079baa6f5a56077a473da0bcf6db9a9e059902e1c854319dcff91fd55427119e72d3c16905a2b4c4acbe25bdecc8b5c6da8a3620a63ee40fc91c1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/ui",
3
- "version": "57.0.7",
3
+ "version": "57.0.9",
4
4
  "description": "A collection of UI components",
5
5
  "sideEffects": [
6
6
  "*.fx.js",
@@ -94,9 +94,9 @@
94
94
  "@types/babel__core": "^7.20.5",
95
95
  "@types/node": "^22.14.0",
96
96
  "@types/react": "~19.2.0",
97
- "react-native-reanimated": "4.5.0",
98
- "react-native-worklets": "0.10.0",
99
- "expo": "57.0.7",
97
+ "react-native-reanimated": "4.5.1",
98
+ "react-native-worklets": "0.10.1",
99
+ "expo": "57.0.10",
100
100
  "expo-module-scripts": "56.0.3"
101
101
  },
102
102
  "jest": {
@@ -121,7 +121,7 @@
121
121
  "optional": true
122
122
  }
123
123
  },
124
- "gitHead": "0dac79c8469fa00b76fb8f402c47ed61f3d6317e",
124
+ "gitHead": "fb5049481ebd5cadd499c4d1f1d5e47f9ac678be",
125
125
  "scripts": {
126
126
  "build": "expo-module build",
127
127
  "clean": "expo-module clean",
@@ -16,6 +16,7 @@ import {
16
16
  presentationBackground,
17
17
  presentationDetents,
18
18
  presentationDragIndicator,
19
+ presentationSizing,
19
20
  } from '../../swift-ui/modifiers/presentationModifiers';
20
21
 
21
22
  export { useBottomSheet } from './context';
@@ -131,7 +132,6 @@ export function BottomSheet(props: BottomSheetProps) {
131
132
  useEffect(() => {
132
133
  if (indexProp === -1) {
133
134
  setIsPresented(false);
134
- fireCloseCallbacks();
135
135
  } else if (indexProp >= 0) {
136
136
  closedRef.current = false;
137
137
  setIsPresented(true);
@@ -139,17 +139,13 @@ export function BottomSheet(props: BottomSheetProps) {
139
139
  setCurrentIndex(clampedIndex);
140
140
  currentIndexRef.current = clampedIndex;
141
141
  }
142
- }, [indexProp, detents.length, fireCloseCallbacks]);
142
+ }, [indexProp, detents.length]);
143
143
 
144
- const handlePresentedChange = useCallback(
145
- (presented: boolean) => {
146
- if (!presented) {
147
- setIsPresented(false);
148
- fireCloseCallbacks();
149
- }
150
- },
151
- [fireCloseCallbacks]
152
- );
144
+ const handlePresentedChange = useCallback((presented: boolean) => {
145
+ if (!presented) {
146
+ setIsPresented(false);
147
+ }
148
+ }, []);
153
149
 
154
150
  const handleDetentChange = useCallback(
155
151
  (detent: PresentationDetent) => {
@@ -167,7 +163,6 @@ export function BottomSheet(props: BottomSheetProps) {
167
163
  const snapToIndex = (index: number) => {
168
164
  if (index === -1) {
169
165
  setIsPresented(false);
170
- fireCloseCallbacks();
171
166
  return;
172
167
  }
173
168
  const clampedIndex = Math.min(Math.max(index, 0), detents.length - 1);
@@ -178,14 +173,11 @@ export function BottomSheet(props: BottomSheetProps) {
178
173
  onChangeRef.current?.(clampedIndex);
179
174
  };
180
175
 
181
- // Fire close callbacks immediately: the native `onIsPresentedChange` event is
182
- // suppressed when JS drives the state change (the Swift layer guards against
183
- // the feedback loop), so we can't rely on handlePresentedChange here. The
184
- // closedRef guard inside fireCloseCallbacks prevents double-firing if a
185
- // native user-dismiss event also arrives during the animation.
176
+ // Close callbacks fire from the native `onDismiss` event, which SwiftUI sends after
177
+ // the dismissal transition finishes, so they can present another modal without UIKit
178
+ // rejecting it. Android awaits `hide()` for the same reason.
186
179
  const close = () => {
187
180
  setIsPresented(false);
188
- fireCloseCallbacks();
189
181
  };
190
182
 
191
183
  return {
@@ -199,7 +191,7 @@ export function BottomSheet(props: BottomSheetProps) {
199
191
  present: () => snapToIndex(0),
200
192
  dismiss: close,
201
193
  };
202
- }, [detents, fireCloseCallbacks]);
194
+ }, [detents]);
203
195
 
204
196
  useImperativeHandle(ref, () => methods, [methods]);
205
197
 
@@ -207,7 +199,8 @@ export function BottomSheet(props: BottomSheetProps) {
207
199
  const bg = extractBackgroundColor(backgroundStyle);
208
200
  return [
209
201
  ...(fitToContents
210
- ? []
202
+ ? // Makes the iPad sheet size to that content instead of opening near full height.
203
+ [presentationSizing('fitted')]
211
204
  : [
212
205
  presentationDetents(detents, {
213
206
  selection: selectedDetent,
@@ -235,6 +228,7 @@ export function BottomSheet(props: BottomSheetProps) {
235
228
  <NativeBottomSheet
236
229
  isPresented={isPresented}
237
230
  onIsPresentedChange={handlePresentedChange}
231
+ onDismiss={fireCloseCallbacks}
238
232
  fitToContents={fitToContents}>
239
233
  <Group modifiers={modifiers}>
240
234
  <RNHostView matchContents={fitToContents}>
@@ -16,7 +16,7 @@ export function MaskedView(props: MaskedViewProps) {
16
16
  // and transforms because the inner views inherit layout from `absoluteFill`.
17
17
  return (
18
18
  <View {...viewProps} style={style}>
19
- <Host style={StyleSheet.absoluteFill}>
19
+ <Host style={StyleSheet.absoluteFill} ignoreSafeArea="all">
20
20
  <Mask alignment="topLeading">
21
21
  <RNHostView>
22
22
  <View style={StyleSheet.absoluteFill}>{children}</View>
@@ -155,6 +155,7 @@ export function MenuView(props: MenuComponentProps & { ref?: React.Ref<MenuCompo
155
155
  const {
156
156
  ref,
157
157
  actions,
158
+ colorScheme,
158
159
  onPressAction,
159
160
  onOpenMenu,
160
161
  onCloseMenu,
@@ -189,7 +190,7 @@ export function MenuView(props: MenuComponentProps & { ref?: React.Ref<MenuCompo
189
190
 
190
191
  return (
191
192
  <View style={style} testID={testID}>
192
- <Host matchContents>
193
+ <Host colorScheme={colorScheme} matchContents>
193
194
  <DropdownMenu expanded={expanded} onDismissRequest={dismissAll}>
194
195
  <DropdownMenu.Trigger>
195
196
  <RNHostView matchContents>
@@ -8,11 +8,7 @@ import { Menu } from '../../swift-ui/Menu';
8
8
  import { RNHostView } from '../../swift-ui/RNHostView';
9
9
  import { Section } from '../../swift-ui/Section';
10
10
  import { Toggle } from '../../swift-ui/Toggle';
11
- import {
12
- disabled as disabledModifier,
13
- foregroundColor as foregroundColorModifier,
14
- tint as tintModifier,
15
- } from '../../swift-ui/modifiers';
11
+ import { disabled as disabledModifier, tint as tintModifier } from '../../swift-ui/modifiers';
16
12
  import type { ModifierConfig } from '../../types';
17
13
 
18
14
  function actionId(action: MenuAction): string {
@@ -34,8 +30,8 @@ function renderAction(
34
30
  const { subactions, displayInline, state, attributes, image, imageColor, title } = action;
35
31
  const key = actionId(action);
36
32
  const systemImage = typeof image === 'string' ? image : undefined;
37
- // `tint` is what SwiftUI's `Menu`/`Button` honor for the leading SF Symbol —
38
- // `foregroundColor` would also affect the label text.
33
+ // `tint` is what SwiftUI's system `Menu` honors for an item's leading SF
34
+ // Symbol, for both `Toggle` and `Button` items.
39
35
  const tintMod = imageColor ? tintModifier(imageColor) : null;
40
36
 
41
37
  if (subactions && subactions.length > 0) {
@@ -49,7 +45,7 @@ function renderAction(
49
45
  }
50
46
  // SwiftUI's system menu UI ignores per-item color modifiers on submenu
51
47
  // headers, so we don't forward `imageColor` here. Leaf `Button`s below
52
- // tint via `foregroundColor`, which does take effect.
48
+ // tint via `.tint`, which does take effect.
53
49
  return (
54
50
  <Menu key={key} label={title} systemImage={systemImage}>
55
51
  {children}
@@ -76,11 +72,13 @@ function renderAction(
76
72
  );
77
73
  }
78
74
 
79
- // For a leaf `Button`, `foregroundColor` also tints the system image —
80
- // upstream uses this to color both the label and the icon. Skip when the
81
- // role is destructive so SwiftUI's red tint remains in effect.
82
- if (imageColor && !attributes?.destructive) {
83
- modifiers.push(foregroundColorModifier(imageColor));
75
+ // Tint the leaf `Button`'s leading SF Symbol via `.tint`, the same modifier
76
+ // the `Toggle` branch above uses. `.foregroundColor` is not applied by the
77
+ // iOS system menu to a menu button's content, so it left action-item icons
78
+ // untinted. Skip when the role is destructive so SwiftUI's red tint remains
79
+ // in effect.
80
+ if (tintMod && !attributes?.destructive) {
81
+ modifiers.push(tintMod);
84
82
  }
85
83
 
86
84
  return (
@@ -1,5 +1,11 @@
1
1
  import type { ReactNode } from 'react';
2
- import type { ColorValue, ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
2
+ import type {
3
+ ColorSchemeName,
4
+ ColorValue,
5
+ ImageSourcePropType,
6
+ StyleProp,
7
+ ViewStyle,
8
+ } from 'react-native';
3
9
  import type { SFSymbol } from 'sf-symbols-typescript';
4
10
 
5
11
  /**
@@ -119,6 +125,11 @@ export type NativeActionEvent = {
119
125
  * Drop-in compatible with `@react-native-menu/menu`.
120
126
  */
121
127
  export interface MenuComponentProps {
128
+ /**
129
+ * The color scheme of the menu on Android. Omit to follow the device setting.
130
+ * @platform android
131
+ */
132
+ colorScheme?: ColorSchemeName;
122
133
  /**
123
134
  * Menu title shown at the top of the menu.
124
135
  * @platform ios
@@ -24,14 +24,14 @@ export interface RNHostProps extends PrimitiveBaseProps {
24
24
  modifiers?: ModifierConfig[];
25
25
  /**
26
26
  * Style applied to the host view's React Native shadow node. Useful for
27
- * controlling its layout position (e.g. `position: 'absolute'`) so the shadow
28
- * layout matches where the hosting Compose component draws the content —
29
- * important for `measure()`-based hit-testing such as `Pressable`.
27
+ * controlling its layout position (e.g. `position: 'absolute'`).
30
28
  */
31
29
  style?: StyleProp<ViewStyle>;
32
30
  }
33
31
 
34
- type NativeRNHostProps = RNHostProps;
32
+ type NativeRNHostProps = RNHostProps & {
33
+ layoutRoot: boolean;
34
+ };
35
35
  const NativeRNHostView: ComponentType<NativeRNHostProps> = requireNativeView(
36
36
  'ExpoUI',
37
37
  'RNHostView'
@@ -43,6 +43,9 @@ function transformProps(props: RNHostProps): NativeRNHostProps {
43
43
  modifiers,
44
44
  ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
45
45
  ...restProps,
46
+ // Touches on hosted content are dispatched relative to the host, so `measure()` must report
47
+ // the same coordinate space; otherwise `Pressable` cancels the press on any finger movement.
48
+ layoutRoot: true,
46
49
  };
47
50
  }
48
51
 
@@ -1,18 +1,22 @@
1
1
  import { requireNativeView } from 'expo';
2
- import { useState, type ComponentType } from 'react';
2
+ import { useState, type ComponentType, type ReactNode } from 'react';
3
3
  import { type NativeSyntheticEvent } from 'react-native';
4
4
 
5
+ import { Slot } from '../SlotView';
5
6
  import { createViewModifierEventListener } from '../modifiers/utils';
6
7
  import { type CommonViewModifierProps } from '../types';
7
8
 
8
9
  export interface BottomSheetProps extends CommonViewModifierProps {
9
10
  /**
10
- * The children of the `BottomSheet` component.
11
- * Use `Group` to wrap your content and apply presentation modifiers
12
- * like `presentationDetents`, `presentationDragIndicator`,
13
- * `presentationBackgroundInteraction`, and `interactiveDismissDisabled`.
11
+ * The sheet's content, mounted while presented and unmounted after dismiss. Wrap it in `Group`
12
+ * to apply presentation modifiers.
14
13
  */
15
- children: React.ReactNode;
14
+ children: ReactNode;
15
+ /**
16
+ * A view the sheet is anchored to, for example the `Button` that opens it. Rendered in place and
17
+ * kept mounted, so presenting the sheet doesn't shift surrounding layout. Optional.
18
+ */
19
+ anchor?: ReactNode;
16
20
  /**
17
21
  * Whether the `BottomSheet` is presented.
18
22
  */
@@ -33,7 +37,10 @@ export interface BottomSheetProps extends CommonViewModifierProps {
33
37
  fitToContents?: boolean;
34
38
  }
35
39
 
36
- type NativeBottomSheetProps = Omit<BottomSheetProps, 'onIsPresentedChange' | 'onDismiss'> & {
40
+ type NativeBottomSheetProps = Omit<
41
+ BottomSheetProps,
42
+ 'onIsPresentedChange' | 'onDismiss' | 'anchor'
43
+ > & {
37
44
  onIsPresentedChange: (event: NativeSyntheticEvent<{ isPresented: boolean }>) => void;
38
45
  onDismiss: (event: NativeSyntheticEvent<object>) => void;
39
46
  };
@@ -47,17 +54,15 @@ const BottomSheetNativeView: ComponentType<NativeBottomSheetProps> = requireNati
47
54
  * `BottomSheet` presents content from the bottom of the screen.
48
55
  */
49
56
  function BottomSheet(props: BottomSheetProps) {
50
- const { modifiers, onIsPresentedChange, onDismiss, ...restProps } = props;
57
+ const { modifiers, onIsPresentedChange, onDismiss, anchor, children, ...restProps } = props;
51
58
  const [isMounted, setIsMounted] = useState(props.isPresented);
52
59
 
53
60
  if (props.isPresented && !isMounted) {
54
61
  setIsMounted(true);
55
62
  }
56
63
 
57
- if (!isMounted) {
58
- return null;
59
- }
60
-
64
+ // The anchor stays mounted so it's always visible; the content mounts on present and unmounts
65
+ // after dismiss.
61
66
  return (
62
67
  <BottomSheetNativeView
63
68
  modifiers={modifiers}
@@ -69,8 +74,10 @@ function BottomSheet(props: BottomSheetProps) {
69
74
  onDismiss={() => {
70
75
  setIsMounted(false);
71
76
  onDismiss?.();
72
- }}
73
- />
77
+ }}>
78
+ {anchor != null ? <Slot name="anchor">{anchor}</Slot> : null}
79
+ {isMounted ? children : null}
80
+ </BottomSheetNativeView>
74
81
  );
75
82
  }
76
83
 
@@ -1743,6 +1743,7 @@ export * from './background';
1743
1743
  export type * from './types';
1744
1744
  export * from './tag';
1745
1745
  export * from './pickerStyle';
1746
+ export * from './menuOrder';
1746
1747
  export * from './tabViewModifiers';
1747
1748
  export * from './datePickerStyle';
1748
1749
  export * from './progressViewStyle';
@@ -0,0 +1,12 @@
1
+ import { createModifier } from './createModifier';
2
+
3
+ export type MenuOrderType = 'automatic' | 'fixed' | 'priority';
4
+
5
+ /**
6
+ * Sets the preferred order of items for menus presented from this view.
7
+ * With the default `automatic` order, a menu that opens upward displays its items
8
+ * in reverse. Pass `fixed` to always keep the order the items were provided in.
9
+ * @param order - The preferred menu item ordering.
10
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/menuorder(_:)).
11
+ */
12
+ export const menuOrder = (order: MenuOrderType) => createModifier('menuOrder', { order });