@expo/ui 58.0.8 → 58.0.10

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 (160) hide show
  1. package/CHANGELOG.md +14 -5
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/AnimatedVisibilityView.kt +40 -8
  4. package/android/src/main/java/expo/modules/ui/ComposeViews.kt +21 -3
  5. package/android/src/main/java/expo/modules/ui/DatePickerView.kt +12 -4
  6. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +5 -1
  7. package/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt +3 -1
  8. package/android/src/main/java/expo/modules/ui/HostView.kt +64 -12
  9. package/android/src/main/java/expo/modules/ui/LazyItemsView.kt +14 -2
  10. package/android/src/main/java/expo/modules/ui/ModalBottomSheetView.kt +5 -1
  11. package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +29 -4
  12. package/android/src/main/java/expo/modules/ui/RNHostView.kt +68 -17
  13. package/android/src/main/java/expo/modules/ui/ShapeView.kt +3 -1
  14. package/android/src/main/java/expo/modules/ui/SlotView.kt +3 -1
  15. package/android/src/main/java/expo/modules/ui/SnackbarView.kt +5 -1
  16. package/android/src/main/java/expo/modules/ui/TextView.kt +3 -1
  17. package/android/src/main/java/expo/modules/ui/Utils.kt +3 -1
  18. package/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt +5 -1
  19. package/android/src/main/java/expo/modules/ui/convertibles/AnimationSpecParams.kt +3 -1
  20. package/android/src/main/java/expo/modules/ui/graphics/ImageLoader.kt +6 -2
  21. package/android/src/main/java/expo/modules/ui/icon/IconView.kt +5 -1
  22. package/android/src/main/java/expo/modules/ui/state/ObservableState.kt +3 -1
  23. package/android/src/main/java/expo/modules/ui/textfield/BasicTextField.kt +10 -2
  24. package/android/src/main/java/expo/modules/ui/textfield/TextField.kt +20 -4
  25. package/android/src/main/java/expo/modules/ui/textfield/TextFieldShared.kt +3 -1
  26. package/build/community/picker/Picker.android.js +7 -0
  27. package/build/community/picker/Picker.android.js.map +1 -1
  28. package/build/jetpack-compose/LazyColumn/index.d.ts +2 -1
  29. package/build/jetpack-compose/LazyColumn/index.js +1 -0
  30. package/build/jetpack-compose/LazyColumn/index.js.map +1 -1
  31. package/build/jetpack-compose/LazyItems/index.d.ts +19 -18
  32. package/build/jetpack-compose/LazyItems/index.js +1 -2
  33. package/build/jetpack-compose/LazyItems/index.js.map +1 -1
  34. package/build/jetpack-compose/LazyRow/index.d.ts +2 -1
  35. package/build/jetpack-compose/LazyRow/index.js +1 -0
  36. package/build/jetpack-compose/LazyRow/index.js.map +1 -1
  37. package/build/jetpack-compose/RNHostView/index.d.ts +6 -1
  38. package/build/jetpack-compose/RNHostView/index.js +9 -4
  39. package/build/jetpack-compose/RNHostView/index.js.map +1 -1
  40. package/build/jetpack-compose/index.d.ts +0 -1
  41. package/build/jetpack-compose/index.js.map +1 -1
  42. package/build/jetpack-compose/modifiers/index.d.ts +18 -3
  43. package/build/jetpack-compose/modifiers/index.js +15 -2
  44. package/build/jetpack-compose/modifiers/index.js.map +1 -1
  45. package/build/swift-ui/LazyHStack/index.d.ts +45 -0
  46. package/build/swift-ui/LazyHStack/index.js +9 -0
  47. package/build/swift-ui/LazyHStack/index.js.map +1 -1
  48. package/build/swift-ui/LazyStackForEach/index.d.ts +44 -0
  49. package/build/swift-ui/LazyStackForEach/index.js +54 -0
  50. package/build/swift-ui/LazyStackForEach/index.js.map +1 -0
  51. package/build/swift-ui/LazyVStack/index.d.ts +45 -0
  52. package/build/swift-ui/LazyVStack/index.js +9 -0
  53. package/build/swift-ui/LazyVStack/index.js.map +1 -1
  54. package/build/swift-ui/List/DataListForEach.d.ts +6 -7
  55. package/build/swift-ui/List/DataListForEach.js.map +1 -1
  56. package/build/swift-ui/List/ListForEach.d.ts +7 -1
  57. package/build/swift-ui/List/ListForEach.js +1 -5
  58. package/build/swift-ui/List/ListForEach.js.map +1 -1
  59. package/build/swift-ui/RNHostView.d.ts +7 -2
  60. package/build/swift-ui/RNHostView.js +7 -3
  61. package/build/swift-ui/RNHostView.js.map +1 -1
  62. package/build/swift-ui/index.d.ts +1 -0
  63. package/build/swift-ui/index.js.map +1 -1
  64. package/build/swift-ui/modifiers/containerBackground.d.ts +2 -2
  65. package/build/swift-ui/modifiers/containerBackground.js +2 -2
  66. package/build/swift-ui/modifiers/index.d.ts +9 -1
  67. package/build/swift-ui/modifiers/index.js +9 -0
  68. package/build/swift-ui/modifiers/index.js.map +1 -1
  69. package/build/universal/ListItem/ListItem.js +11 -2
  70. package/build/universal/ListItem/ListItem.js.map +1 -1
  71. package/build/universal/RNHostView/index.js +12 -5
  72. package/build/universal/RNHostView/index.js.map +1 -1
  73. package/build/universal/RNHostView/types.d.ts +9 -4
  74. package/build/universal/RNHostView/types.js.map +1 -1
  75. package/build/universal/Switch/index.android.js +5 -1
  76. package/build/universal/Switch/index.android.js.map +1 -1
  77. package/build/utils/matchContents.d.ts +10 -0
  78. package/build/utils/matchContents.js +16 -0
  79. package/build/utils/matchContents.js.map +1 -0
  80. package/expo-module.config.json +0 -1
  81. package/ios/DataListForEachView.swift +65 -28
  82. package/ios/ExpoUIModule.swift +0 -1
  83. package/ios/LazyHStackView.swift +3 -0
  84. package/ios/LazyVStackView.swift +3 -0
  85. package/ios/ListView.swift +8 -0
  86. package/ios/Modifiers/ContainerBackgroundModifier.swift +23 -16
  87. package/ios/Modifiers/PreferredColorSchemeModifier.swift +12 -0
  88. package/ios/Modifiers/View+ModifierArray.swift +3 -3
  89. package/ios/Modifiers/ViewModifierRegistry.swift +11 -9
  90. package/ios/Modifiers/WidgetKitModifiers.swift +13 -0
  91. package/ios/RNHostView.swift +55 -9
  92. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10-sources.jar +0 -0
  93. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10-sources.jar.md5 +1 -0
  94. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10-sources.jar.sha1 +1 -0
  95. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10-sources.jar.sha256 +1 -0
  96. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10-sources.jar.sha512 +1 -0
  97. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.aar +0 -0
  98. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.aar.md5 +1 -0
  99. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.aar.sha1 +1 -0
  100. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.aar.sha256 +1 -0
  101. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.aar.sha512 +1 -0
  102. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.8/expo.modules.ui-58.0.8.module → 58.0.10/expo.modules.ui-58.0.10.module} +22 -22
  103. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.module.md5 +1 -0
  104. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.module.sha1 +1 -0
  105. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.module.sha256 +1 -0
  106. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.module.sha512 +1 -0
  107. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.8/expo.modules.ui-58.0.8.pom → 58.0.10/expo.modules.ui-58.0.10.pom} +1 -1
  108. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.pom.md5 +1 -0
  109. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.pom.sha1 +1 -0
  110. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.pom.sha256 +1 -0
  111. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.10/expo.modules.ui-58.0.10.pom.sha512 +1 -0
  112. package/package.json +3 -3
  113. package/prebuilds/output/debug/xcframeworks/ExpoUI.tar.gz +0 -0
  114. package/prebuilds/output/release/xcframeworks/ExpoUI.tar.gz +0 -0
  115. package/src/community/picker/Picker.android.tsx +5 -0
  116. package/src/jetpack-compose/LazyColumn/index.tsx +2 -0
  117. package/src/jetpack-compose/LazyItems/index.tsx +18 -21
  118. package/src/jetpack-compose/LazyRow/index.tsx +2 -0
  119. package/src/jetpack-compose/RNHostView/index.tsx +17 -6
  120. package/src/jetpack-compose/index.ts +0 -1
  121. package/src/jetpack-compose/modifiers/index.ts +16 -3
  122. package/src/swift-ui/LazyHStack/index.tsx +45 -0
  123. package/src/swift-ui/LazyStackForEach/index.tsx +110 -0
  124. package/src/swift-ui/LazyVStack/index.tsx +45 -0
  125. package/src/swift-ui/List/DataListForEach.tsx +7 -8
  126. package/src/swift-ui/List/ListForEach.tsx +10 -3
  127. package/src/swift-ui/RNHostView.tsx +11 -4
  128. package/src/swift-ui/index.tsx +1 -0
  129. package/src/swift-ui/modifiers/containerBackground.ts +2 -2
  130. package/src/swift-ui/modifiers/index.ts +11 -0
  131. package/src/universal/ListItem/ListItem.tsx +10 -1
  132. package/src/universal/RNHostView/index.tsx +8 -3
  133. package/src/universal/RNHostView/types.ts +6 -4
  134. package/src/universal/Switch/index.android.tsx +11 -2
  135. package/src/utils/matchContents.ts +14 -0
  136. package/ios/AccessoryWidgetBackgroundView.swift +0 -27
  137. package/ios/Modifiers/WidgetModifiers.swift +0 -70
  138. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8-sources.jar +0 -0
  139. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8-sources.jar.md5 +0 -1
  140. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8-sources.jar.sha1 +0 -1
  141. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8-sources.jar.sha256 +0 -1
  142. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8-sources.jar.sha512 +0 -1
  143. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.aar +0 -0
  144. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.aar.md5 +0 -1
  145. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.aar.sha1 +0 -1
  146. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.aar.sha256 +0 -1
  147. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.aar.sha512 +0 -1
  148. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.module.md5 +0 -1
  149. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.module.sha1 +0 -1
  150. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.module.sha256 +0 -1
  151. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.module.sha512 +0 -1
  152. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.pom.md5 +0 -1
  153. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.pom.sha1 +0 -1
  154. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.pom.sha256 +0 -1
  155. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.8/expo.modules.ui-58.0.8.pom.sha512 +0 -1
  156. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +0 -13
  157. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +0 -1
  158. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +0 -1
  159. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +0 -1
  160. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -1,14 +1,23 @@
1
1
  # Changelog
2
2
 
3
- ## Unpublished
3
+ ## 58.0.10
4
4
 
5
- ### 🛠 Breaking changes
5
+ ### Patch Changes
6
6
 
7
- ### 🎉 New features
7
+ - [android] Fix `community/picker` `Picker` not updating its displayed label after a selection or a `selectedValue` change. ([#50801](https://github.com/expo/expo/pull/50801) by [@RaddishIoW](https://github.com/RaddishIoW))
8
+ - [web] Fix `ListItem` always showing a pointer cursor, even without an `onPress`, because React Native Web's `Pressable` applies one unconditionally. A `ListItem` with no `onPress` now shows the default cursor instead of misleadingly looking clickable. ([#49986](https://github.com/expo/expo/pull/49986) by [@timheilman](https://github.com/timheilman))
9
+ - [iOS][Android] `RNHostView` `matchContents` accepts `{ horizontal, vertical }`, so hosted content can take its width from the parent and its height from the content. ([#50674](https://github.com/expo/expo/pull/50674) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
10
+ - [iOS] Stop linking WidgetKit into apps that use `@expo/ui` without widgets. The `widgetURL`, `activityBackgroundTint`, `widgetAccentedRenderingMode`, and `containerBackground` (`widget` placement) modifiers and the `AccessoryWidgetBackground` view now take effect only inside `expo-widgets` widgets and Live Activities. ([#50786](https://github.com/expo/expo/pull/50786) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
11
+ - [iOS] Added the `preferredColorScheme` modifier, which sets the color scheme of a presentation such as `BottomSheet`, including its background. ([#50851](https://github.com/expo/expo/pull/50851) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
8
12
 
9
- ### 🐛 Bug fixes
13
+ ## 58.0.9
10
14
 
11
- ### 💡 Others
15
+ ### Patch Changes
16
+
17
+ - Force-bump all packages, due to migration to changesets. ([#50762](https://github.com/expo/expo/pull/50762) by [@kitten](https://github.com/kitten))
18
+ - [iOS] Added `LazyVStack.ForEach` and `LazyHStack.ForEach`, which render rows from `data` and `keyExtractor` with a `children` function: `{({ item, index }) => <Row item={item} />}`. Rows are recycled from a small pool around the visible range, so large stacks only render the rows near the viewport. Set `recycling={false}` to render every row. ([#50579](https://github.com/expo/expo/pull/50579) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
19
+ - [android] Fix `Switch` label rendering one letter per line inside a `Host` with `matchContents`. ([#50693](https://github.com/expo/expo/pull/50693) by [@janicduplessis](https://github.com/janicduplessis))
20
+ - [android] Added `IntrinsicSize` support to the `width` modifier. ([#50693](https://github.com/expo/expo/pull/50693) by [@janicduplessis](https://github.com/janicduplessis))
12
21
 
13
22
  ## 58.0.8 — 2026-09-28
14
23
 
@@ -12,13 +12,13 @@ apply plugin: 'expo-module-gradle-plugin'
12
12
  apply plugin: 'org.jetbrains.kotlin.plugin.compose'
13
13
 
14
14
  group = 'expo.modules.ui'
15
- version = '58.0.8'
15
+ version = '58.0.10'
16
16
 
17
17
  android {
18
18
  namespace "expo.modules.ui"
19
19
  defaultConfig {
20
20
  versionCode 1
21
- versionName "58.0.8"
21
+ versionName "58.0.10"
22
22
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
23
23
  }
24
24
  buildFeatures {
@@ -73,32 +73,64 @@ data class ExitTransitionRecord(
73
73
  // TODO(@ubax): expose animationSpec parameter (tween, spring, duration) for all transitions
74
74
  private fun EnterTransitionRecord.toComposeTransition(): EnterTransition = when (type) {
75
75
  EnterTransitionType.FADE_IN ->
76
- if (initialAlpha != null) fadeIn(initialAlpha = initialAlpha) else fadeIn()
76
+ if (initialAlpha != null) {
77
+ fadeIn(initialAlpha = initialAlpha)
78
+ } else {
79
+ fadeIn()
80
+ }
77
81
  EnterTransitionType.SLIDE_IN_HORIZONTALLY ->
78
- if (initialOffsetX != null) slideInHorizontally { (it * initialOffsetX).toInt() } else slideInHorizontally()
82
+ if (initialOffsetX != null) {
83
+ slideInHorizontally { (it * initialOffsetX).toInt() }
84
+ } else {
85
+ slideInHorizontally()
86
+ }
79
87
  EnterTransitionType.SLIDE_IN_VERTICALLY ->
80
- if (initialOffsetY != null) slideInVertically { (it * initialOffsetY).toInt() } else slideInVertically()
88
+ if (initialOffsetY != null) {
89
+ slideInVertically { (it * initialOffsetY).toInt() }
90
+ } else {
91
+ slideInVertically()
92
+ }
81
93
  // TODO(@ubax): expose expandFrom (Alignment) and initialSize parameters for expand transitions
82
94
  EnterTransitionType.EXPAND_IN -> expandIn()
83
95
  EnterTransitionType.EXPAND_HORIZONTALLY -> expandHorizontally()
84
96
  EnterTransitionType.EXPAND_VERTICALLY -> expandVertically()
85
97
  EnterTransitionType.SCALE_IN ->
86
- if (initialScale != null) scaleIn(initialScale = initialScale) else scaleIn()
98
+ if (initialScale != null) {
99
+ scaleIn(initialScale = initialScale)
100
+ } else {
101
+ scaleIn()
102
+ }
87
103
  }
88
104
 
89
105
  private fun ExitTransitionRecord.toComposeTransition(): ExitTransition = when (type) {
90
106
  ExitTransitionType.FADE_OUT ->
91
- if (targetAlpha != null) fadeOut(targetAlpha = targetAlpha) else fadeOut()
107
+ if (targetAlpha != null) {
108
+ fadeOut(targetAlpha = targetAlpha)
109
+ } else {
110
+ fadeOut()
111
+ }
92
112
  ExitTransitionType.SLIDE_OUT_HORIZONTALLY ->
93
- if (targetOffsetX != null) slideOutHorizontally { (it * targetOffsetX).toInt() } else slideOutHorizontally()
113
+ if (targetOffsetX != null) {
114
+ slideOutHorizontally { (it * targetOffsetX).toInt() }
115
+ } else {
116
+ slideOutHorizontally()
117
+ }
94
118
  ExitTransitionType.SLIDE_OUT_VERTICALLY ->
95
- if (targetOffsetY != null) slideOutVertically { (it * targetOffsetY).toInt() } else slideOutVertically()
119
+ if (targetOffsetY != null) {
120
+ slideOutVertically { (it * targetOffsetY).toInt() }
121
+ } else {
122
+ slideOutVertically()
123
+ }
96
124
  // TODO(@ubax): expose shrinkTowards (Alignment) and targetSize parameters for shrink transitions
97
125
  ExitTransitionType.SHRINK_OUT -> shrinkOut()
98
126
  ExitTransitionType.SHRINK_HORIZONTALLY -> shrinkHorizontally()
99
127
  ExitTransitionType.SHRINK_VERTICALLY -> shrinkVertically()
100
128
  ExitTransitionType.SCALE_OUT ->
101
- if (targetScale != null) scaleOut(targetScale = targetScale) else scaleOut()
129
+ if (targetScale != null) {
130
+ scaleOut(targetScale = targetScale)
131
+ } else {
132
+ scaleOut()
133
+ }
102
134
  }
103
135
 
104
136
  private fun List<EnterTransitionRecord>.toComposedEnterTransition(): EnterTransition? =
@@ -65,7 +65,13 @@ internal fun FunctionalComposableScope.RowContent(props: LayoutProps) {
65
65
  verticalAlignment = props.verticalAlignment?.toComposeAlignment() ?: Alignment.Top,
66
66
  modifier = ModifierRegistry
67
67
  .applyModifiers(props.modifiers, appContext, composableScope, globalEventDispatcher)
68
- .then(if (scrollBehavior != null) Modifier.nestedScroll(scrollBehavior) else Modifier)
68
+ .then(
69
+ if (scrollBehavior != null) {
70
+ Modifier.nestedScroll(scrollBehavior)
71
+ } else {
72
+ Modifier
73
+ }
74
+ )
69
75
  ) {
70
76
  Children(UIComposableScope(rowScope = this@Row, nestedScrollConnection = scrollBehavior))
71
77
  }
@@ -95,7 +101,13 @@ internal fun FunctionalComposableScope.ColumnContent(props: LayoutProps) {
95
101
  horizontalAlignment = props.horizontalAlignment?.toComposeAlignment() ?: Alignment.Start,
96
102
  modifier = ModifierRegistry
97
103
  .applyModifiers(props.modifiers, appContext, composableScope, globalEventDispatcher)
98
- .then(if (scrollBehavior != null) Modifier.nestedScroll(scrollBehavior) else Modifier)
104
+ .then(
105
+ if (scrollBehavior != null) {
106
+ Modifier.nestedScroll(scrollBehavior)
107
+ } else {
108
+ Modifier
109
+ }
110
+ )
99
111
  ) {
100
112
  Children(UIComposableScope(columnScope = this@Column, nestedScrollConnection = scrollBehavior))
101
113
  }
@@ -112,7 +124,13 @@ fun FunctionalComposableScope.BoxContent(props: LayoutProps) {
112
124
  contentAlignment = props.contentAlignment?.toComposeAlignment() ?: Alignment.TopStart,
113
125
  modifier = ModifierRegistry
114
126
  .applyModifiers(props.modifiers, appContext, composableScope, globalEventDispatcher)
115
- .then(if (scrollBehavior != null) Modifier.nestedScroll(scrollBehavior) else Modifier)
127
+ .then(
128
+ if (scrollBehavior != null) {
129
+ Modifier.nestedScroll(scrollBehavior)
130
+ } else {
131
+ Modifier
132
+ }
133
+ )
116
134
  ) {
117
135
  Children(UIComposableScope(boxScope = this@Box, nestedScrollConnection = scrollBehavior))
118
136
  }
@@ -219,14 +219,22 @@ fun rememberSelectableDates(selectableDatesRecord: SelectableDatesRecord?): Sele
219
219
 
220
220
  object : SelectableDates {
221
221
  override fun isSelectableDate(utcTimeMillis: Long): Boolean {
222
- if (startUtcDayMillis != null && utcTimeMillis < startUtcDayMillis) return false
223
- if (endUtcDayMillis != null && utcTimeMillis > endUtcDayMillis) return false
222
+ if (startUtcDayMillis != null && utcTimeMillis < startUtcDayMillis) {
223
+ return false
224
+ }
225
+ if (endUtcDayMillis != null && utcTimeMillis > endUtcDayMillis) {
226
+ return false
227
+ }
224
228
  return true
225
229
  }
226
230
 
227
231
  override fun isSelectableYear(year: Int): Boolean {
228
- if (startYear != null && year < startYear) return false
229
- if (endYear != null && year > endYear) return false
232
+ if (startYear != null && year < startYear) {
233
+ return false
234
+ }
235
+ if (endYear != null && year > endYear) {
236
+ return false
237
+ }
230
238
  return true
231
239
  }
232
240
  }
@@ -167,7 +167,11 @@ class ExpoUIModule : Module() {
167
167
  ?: appContext.reactContext
168
168
  ?: throw Exceptions.ReactContextLost()
169
169
  val resolvedScheme = options?.scheme
170
- ?: if (context.isSystemInDarkTheme()) ExpoColorScheme.DARK else ExpoColorScheme.LIGHT
170
+ ?: if (context.isSystemInDarkTheme()) {
171
+ ExpoColorScheme.DARK
172
+ } else {
173
+ ExpoColorScheme.LIGHT
174
+ }
171
175
  val isDark = resolvedScheme == ExpoColorScheme.DARK
172
176
  val seedArgb = options?.seedColor?.composeOrNull?.toArgb()
173
177
  val colorScheme = if (seedArgb != null) {
@@ -125,7 +125,9 @@ fun FunctionalComposableScope.HorizontalPagerContent(
125
125
  }
126
126
 
127
127
  val pageCount = pageCountState.intValue
128
- if (pageCount == 0) return
128
+ if (pageCount == 0) {
129
+ return
130
+ }
129
131
 
130
132
  // Mirror Compose's PagerState observable fields to JS callbacks. Each
131
133
  // state-backed snapshotFlow drops its first emission so we don't echo the
@@ -81,17 +81,33 @@ internal enum class ExpoColorScheme(val value: String) : Enumerable {
81
81
 
82
82
  fun toColorScheme(context: Context): ColorScheme {
83
83
  return when (this) {
84
- LIGHT -> if (isDynamicColorSupported) dynamicLightColorScheme(context) else lightColorScheme()
85
- DARK -> if (isDynamicColorSupported) dynamicDarkColorScheme(context) else darkColorScheme()
84
+ LIGHT -> if (isDynamicColorSupported) {
85
+ dynamicLightColorScheme(context)
86
+ } else {
87
+ lightColorScheme()
88
+ }
89
+ DARK -> if (isDynamicColorSupported) {
90
+ dynamicDarkColorScheme(context)
91
+ } else {
92
+ darkColorScheme()
93
+ }
86
94
  }
87
95
  }
88
96
 
89
97
  companion object {
90
98
  fun defaultColorScheme(context: Context, isSystemInDarkTheme: Boolean): ColorScheme {
91
99
  return if (isDynamicColorSupported) {
92
- if (isSystemInDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
100
+ if (isSystemInDarkTheme) {
101
+ dynamicDarkColorScheme(context)
102
+ } else {
103
+ dynamicLightColorScheme(context)
104
+ }
93
105
  } else {
94
- if (isSystemInDarkTheme) darkColorScheme() else lightColorScheme()
106
+ if (isSystemInDarkTheme) {
107
+ darkColorScheme()
108
+ } else {
109
+ lightColorScheme()
110
+ }
95
111
  }
96
112
  }
97
113
  }
@@ -159,8 +175,20 @@ internal class HostView(context: Context, appContext: AppContext) :
159
175
 
160
176
  Layout(
161
177
  modifier = Modifier
162
- .then(if (props.matchContentsHorizontal.value == true) Modifier.wrapContentWidth() else Modifier)
163
- .then(if (props.matchContentsVertical.value == true) Modifier.wrapContentHeight() else Modifier)
178
+ .then(
179
+ if (props.matchContentsHorizontal.value == true) {
180
+ Modifier.wrapContentWidth()
181
+ } else {
182
+ Modifier
183
+ }
184
+ )
185
+ .then(
186
+ if (props.matchContentsVertical.value == true) {
187
+ Modifier.wrapContentHeight()
188
+ } else {
189
+ Modifier
190
+ }
191
+ )
164
192
  .onSizeChanged { size -> dispatchOnLayoutContent(size, density) },
165
193
  content = content
166
194
  ) { measurables, constraints ->
@@ -199,8 +227,16 @@ internal class HostView(context: Context, appContext: AppContext) :
199
227
  val heightDp = contentHeightPx.toDp().value.toDouble()
200
228
 
201
229
  shadowNodeProxy.setViewSize(
202
- if (constraints.maxWidth == 0) widthDp else Double.NaN,
203
- if (constraints.maxHeight == 0) heightDp else Double.NaN
230
+ if (constraints.maxWidth == 0) {
231
+ widthDp
232
+ } else {
233
+ Double.NaN
234
+ },
235
+ if (constraints.maxHeight == 0) {
236
+ heightDp
237
+ } else {
238
+ Double.NaN
239
+ }
204
240
  )
205
241
  }
206
242
  }
@@ -227,8 +263,16 @@ internal class HostView(context: Context, appContext: AppContext) :
227
263
  val height = size.height.toDp().value
228
264
 
229
265
  if (matchContentsHorizontal == true || matchContentsVertical == true) {
230
- val styleWidth = if (matchContentsHorizontal == true && width > 0) width else null
231
- val styleHeight = if (matchContentsVertical == true && height > 0) height else null
266
+ val styleWidth = if (matchContentsHorizontal == true && width > 0) {
267
+ width
268
+ } else {
269
+ null
270
+ }
271
+ val styleHeight = if (matchContentsVertical == true && height > 0) {
272
+ height
273
+ } else {
274
+ null
275
+ }
232
276
  shadowNodeProxy.setStyleSize(styleWidth?.toDouble(), styleHeight?.toDouble())
233
277
  }
234
278
 
@@ -263,8 +307,16 @@ internal class HostView(context: Context, appContext: AppContext) :
263
307
  val matchContentsVertical = props.matchContentsVertical.value
264
308
  val composeView = findComposeView()
265
309
  composeView.layoutParams = LayoutParams(
266
- if (matchContentsHorizontal == true) LayoutParams.WRAP_CONTENT else LayoutParams.MATCH_PARENT,
267
- if (matchContentsVertical == true) LayoutParams.WRAP_CONTENT else LayoutParams.MATCH_PARENT
310
+ if (matchContentsHorizontal == true) {
311
+ LayoutParams.WRAP_CONTENT
312
+ } else {
313
+ LayoutParams.MATCH_PARENT
314
+ },
315
+ if (matchContentsVertical == true) {
316
+ LayoutParams.WRAP_CONTENT
317
+ } else {
318
+ LayoutParams.MATCH_PARENT
319
+ }
268
320
  )
269
321
  }
270
322
 
@@ -176,7 +176,15 @@ internal fun LazyListScope.lazyRecycledItems(
176
176
  Box(
177
177
  modifier = Modifier.onSizeChanged { size ->
178
178
  if (content != null) {
179
- window.measure(itemKey, if (isVertical) size.height else size.width, revision)
179
+ window.measure(
180
+ itemKey,
181
+ if (isVertical) {
182
+ size.height
183
+ } else {
184
+ size.width
185
+ },
186
+ revision
187
+ )
180
188
  }
181
189
  }
182
190
  ) {
@@ -205,7 +213,11 @@ internal fun LazyListScope.lazyRecycledItems(
205
213
  */
206
214
  internal fun Modifier.lazyRecycledItemsCrossAxis(list: ViewGroup, isVertical: Boolean): Modifier =
207
215
  layout { measurable, constraints ->
208
- val crossAxisSize = if (isVertical) constraints.maxWidth else constraints.maxHeight
216
+ val crossAxisSize = if (isVertical) {
217
+ constraints.maxWidth
218
+ } else {
219
+ constraints.maxHeight
220
+ }
209
221
  for (index in 0..<list.childCount) {
210
222
  (list.getChildAt(index) as? LazyItemsView)?.window?.updateCrossAxisSize(crossAxisSize)
211
223
  }
@@ -145,7 +145,11 @@ fun FunctionalComposableScope.ModalBottomSheetContent(
145
145
  // then asked for light system bars, which turned the navigation bar white.
146
146
  val resolvedContentColor = props.contentColor.composeOrNull
147
147
  ?: MaterialTheme.colorScheme.contentColorFor(resolvedContainerColor).takeOrElse {
148
- if (resolvedContainerColor.luminance() > 0.5f) ComposeColor.Black else ComposeColor.White
148
+ if (resolvedContainerColor.luminance() > 0.5f) {
149
+ ComposeColor.Black
150
+ } else {
151
+ ComposeColor.White
152
+ }
149
153
  }
150
154
  val resolvedScrimColor = props.scrimColor.composeOrNull ?: BottomSheetDefaults.ScrimColor
151
155
  val dragHandleSlotView = findChildSlotView(view, "dragHandle")
@@ -21,6 +21,7 @@ import androidx.compose.foundation.border
21
21
  import androidx.compose.foundation.clickable
22
22
  import androidx.compose.foundation.combinedClickable
23
23
  import androidx.compose.foundation.horizontalScroll
24
+ import androidx.compose.foundation.layout.IntrinsicSize
24
25
  import androidx.compose.foundation.layout.defaultMinSize
25
26
  import androidx.compose.foundation.layout.fillMaxHeight
26
27
  import androidx.compose.foundation.layout.fillMaxSize
@@ -79,6 +80,7 @@ import expo.modules.kotlin.records.Field
79
80
  import expo.modules.kotlin.records.Record
80
81
  import expo.modules.kotlin.records.recordFromMap
81
82
  import expo.modules.kotlin.types.ConverterContext
83
+ import expo.modules.kotlin.types.Either
82
84
  import expo.modules.kotlin.types.Enumerable
83
85
  import expo.modules.kotlin.types.OptimizedRecord
84
86
  import expo.modules.kotlin.views.ComposableScope
@@ -133,6 +135,21 @@ data class WidthParams(
133
135
  @Field val width: Int = 0
134
136
  ) : Record
135
137
 
138
+ internal enum class IntrinsicSizeType(val value: String) : Enumerable {
139
+ MIN("min"),
140
+ MAX("max");
141
+
142
+ fun toComposeIntrinsicSize(): IntrinsicSize = when (this) {
143
+ MIN -> IntrinsicSize.Min
144
+ MAX -> IntrinsicSize.Max
145
+ }
146
+ }
147
+
148
+ @OptimizedRecord
149
+ internal data class ComposeWidthParams(
150
+ @Field val width: Either<Int, IntrinsicSizeType>? = null
151
+ ) : Record
152
+
136
153
  @OptimizedRecord
137
154
  data class HeightParams(
138
155
  @Field val height: Int = 0
@@ -168,7 +185,9 @@ data class BackgroundParams(
168
185
  // Color animation specs reuse the JS `$type` shape from `@expo/ui/jetpack-compose/modifiers/animation` (spring / tween / snap).
169
186
  // Keyframes are float-only and aren't supported for colors.
170
187
  private fun parseColorAnimationSpec(raw: Any?): AnimationSpec<androidx.compose.ui.graphics.Color>? {
171
- if (raw !is Map<*, *>) return null
188
+ if (raw !is Map<*, *>) {
189
+ return null
190
+ }
172
191
  return when (raw["\$type"]) {
173
192
  "spring" -> spring(
174
193
  dampingRatio = (raw["dampingRatio"] as? Number)?.toFloat() ?: Spring.DampingRatioNoBouncy,
@@ -409,7 +428,9 @@ object ModifierRegistry {
409
428
  scope: ComposableScope,
410
429
  eventDispatcher: ModifierEventDispatcher
411
430
  ): Modifier {
412
- if (modifiers.isNullOrEmpty()) return Modifier
431
+ if (modifiers.isNullOrEmpty()) {
432
+ return Modifier
433
+ }
413
434
  return modifiers.fold(Modifier as Modifier) { acc, config ->
414
435
  val type = config["\$type"]?.asString() ?: return@fold acc
415
436
  val modifier = modifierFactories[type]?.invoke(config, scope, appContext, eventDispatcher)
@@ -471,8 +492,12 @@ object ModifierRegistry {
471
492
  }
472
493
 
473
494
  register("width") { map, _, appContext, _ ->
474
- val params = recordFromMap<WidthParams>(map, appContext)
475
- Modifier.width(params.width.dp)
495
+ val width = recordFromMap<ComposeWidthParams>(map, appContext).width
496
+ if (width?.`is`(IntrinsicSizeType::class) == true) {
497
+ Modifier.width(width.second().toComposeIntrinsicSize())
498
+ } else {
499
+ Modifier.width((width?.first() ?: 0).dp)
500
+ }
476
501
  }
477
502
 
478
503
  register("height") { map, _, appContext, _ ->
@@ -8,9 +8,15 @@ import android.view.View
8
8
  import android.view.ViewGroup
9
9
  import android.view.ViewParent
10
10
  import android.widget.FrameLayout
11
+ import androidx.compose.foundation.layout.fillMaxHeight
11
12
  import androidx.compose.foundation.layout.fillMaxSize
13
+ import androidx.compose.foundation.layout.fillMaxWidth
14
+ import androidx.compose.foundation.layout.requiredHeight
12
15
  import androidx.compose.foundation.layout.requiredSize
16
+ import androidx.compose.foundation.layout.requiredWidth
17
+ import androidx.compose.foundation.layout.wrapContentHeight
13
18
  import androidx.compose.foundation.layout.wrapContentSize
19
+ import androidx.compose.foundation.layout.wrapContentWidth
14
20
  import androidx.compose.runtime.Composable
15
21
  import androidx.compose.runtime.MutableState
16
22
  import androidx.compose.runtime.mutableStateOf
@@ -42,7 +48,8 @@ import expo.modules.kotlin.views.OptimizedComposeProps
42
48
 
43
49
  @OptimizedComposeProps
44
50
  internal data class RNHostViewProps(
45
- val matchContents: MutableState<Boolean?> = mutableStateOf(null),
51
+ val matchContentsHorizontal: MutableState<Boolean?> = mutableStateOf(null),
52
+ val matchContentsVertical: MutableState<Boolean?> = mutableStateOf(null),
46
53
  // Adds LeafNode and MeasurableYogaNode trait in Shadow node
47
54
  val expoInternalSizeFromChildren: MutableState<Boolean?> = mutableStateOf(null),
48
55
  val modifiers: MutableState<ModifierList> = mutableStateOf(emptyList())
@@ -146,22 +153,33 @@ internal class RNHostView(context: Context, appContext: AppContext) :
146
153
 
147
154
  @Composable
148
155
  override fun ComposableScope.Content() {
149
- val matchContents = props.matchContents.value ?: false
156
+ val matchContentsHorizontal = props.matchContentsHorizontal.value ?: false
157
+ val matchContentsVertical = props.matchContentsVertical.value ?: false
150
158
  val scope: ComposableScope = this
151
159
 
152
160
  wrapperState.value?.let { wrapper ->
153
161
  childViewState.value ?: return@let
154
- val sizingModifier = if (matchContents) {
155
- applySizeFromYogaNodeModifier()
156
- } else {
157
- Modifier
158
- .fillMaxSize()
159
- .then(reportSizeToYogaNodeModifier())
162
+ val sizingModifier = when {
163
+ matchContentsHorizontal && matchContentsVertical -> applySizeFromYogaNodeModifier()
164
+ matchContentsVertical ->
165
+ Modifier
166
+ .fillMaxWidth()
167
+ .then(reportSizeToYogaNodeModifier(reportHeight = false))
168
+ .then(applyHeightFromYogaNodeModifier())
169
+ matchContentsHorizontal ->
170
+ Modifier
171
+ .fillMaxHeight()
172
+ .then(reportSizeToYogaNodeModifier(reportWidth = false))
173
+ .then(applyWidthFromYogaNodeModifier())
174
+ else ->
175
+ Modifier
176
+ .fillMaxSize()
177
+ .then(reportSizeToYogaNodeModifier())
160
178
  }
161
- // Origin last: a chain applies outside-in, so a caller `padding` or `offset` has to shift the
162
- // content before it is read.
163
- val modifiers = sizingModifier
164
- .then(ModifierRegistry.applyModifiers(props.modifiers.value, appContext, scope, globalEventDispatcher))
179
+ // A chain applies outside-in. Caller modifiers go first so a `padding` or `border` shrinks the
180
+ // box before its size is reported and its origin is read.
181
+ val modifiers = ModifierRegistry.applyModifiers(props.modifiers.value, appContext, scope, globalEventDispatcher)
182
+ .then(sizingModifier)
165
183
  .then(publishContentOriginModifier())
166
184
 
167
185
  AndroidView(
@@ -204,6 +222,28 @@ internal class RNHostView(context: Context, appContext: AppContext) :
204
222
  }
205
223
  }
206
224
 
225
+ @Composable
226
+ private fun applyHeightFromYogaNodeModifier(): Modifier {
227
+ val height = childSizeState.value.height
228
+ if (height <= 0) {
229
+ return Modifier
230
+ }
231
+ return Modifier
232
+ .wrapContentHeight(Alignment.Top, unbounded = true)
233
+ .requiredHeight(with(LocalDensity.current) { height.toDp() })
234
+ }
235
+
236
+ @Composable
237
+ private fun applyWidthFromYogaNodeModifier(): Modifier {
238
+ val width = childSizeState.value.width
239
+ if (width <= 0) {
240
+ return Modifier
241
+ }
242
+ return Modifier
243
+ .wrapContentWidth(Alignment.CenterHorizontally, unbounded = true)
244
+ .requiredWidth(with(LocalDensity.current) { width.toDp() })
245
+ }
246
+
207
247
  /**
208
248
  * Publishes where Compose placed this view inside its `Host`, which Yoga has no way to know:
209
249
  * Yoga puts the box at the `Host`'s origin while Compose may draw it anywhere inside. Without
@@ -233,13 +273,21 @@ internal class RNHostView(context: Context, appContext: AppContext) :
233
273
  // Sets Yoga node size from Compose view size
234
274
  // Listens to Compose view size changes and updates the Yoga node size
235
275
  @Composable
236
- private fun reportSizeToYogaNodeModifier(): Modifier {
276
+ private fun reportSizeToYogaNodeModifier(reportWidth: Boolean = true, reportHeight: Boolean = true): Modifier {
237
277
  val density = LocalDensity.current
238
278
  return Modifier.onSizeChanged { size ->
239
279
  with(density) {
240
280
  shadowNodeProxy.setViewSize(
241
- size.width.toDp().value.toDouble(),
242
- size.height.toDp().value.toDouble()
281
+ if (reportWidth) {
282
+ size.width.toDp().value.toDouble()
283
+ } else {
284
+ Double.NaN
285
+ },
286
+ if (reportHeight) {
287
+ size.height.toDp().value.toDouble()
288
+ } else {
289
+ Double.NaN
290
+ }
243
291
  )
244
292
  }
245
293
  }
@@ -318,7 +366,8 @@ private class TouchDispatchingRootViewGroup(
318
366
 
319
367
  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
320
368
  // Always gets called with EXACTLY mode
321
- // because parent has either fillMaxSize (matchContents = false) or requiredSize (matchContents = true) modifiers
369
+ // because parent has fillMaxSize (matchContents = false), requiredSize (matchContents = true), or a
370
+ // fillMax* + required* pair (one matched axis) modifiers
322
371
  setMeasuredDimension(
323
372
  MeasureSpec.getSize(widthMeasureSpec),
324
373
  MeasureSpec.getSize(heightMeasureSpec)
@@ -498,7 +547,9 @@ private class TouchDispatchingRootViewGroup(
498
547
  // Use the (…, type, consumed) variant so we can read how much the sheet ate (consumed[1]).
499
548
  val consumed = IntArray(2)
500
549
  dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null, ViewCompat.TYPE_TOUCH, consumed)
501
- if (consumed[1] != 0) sheetMovingOnLastDragFrame = true
550
+ if (consumed[1] != 0) {
551
+ sheetMovingOnLastDragFrame = true
552
+ }
502
553
  }
503
554
 
504
555
  override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
@@ -179,7 +179,9 @@ fun pathFromShapeRecord(record: ShapeRecord, size: Size, density: Density): Path
179
179
  }
180
180
 
181
181
  fun shapeFromShapeRecord(shapeRecord: ShapeRecord?): Shape? {
182
- if (shapeRecord == null) return null
182
+ if (shapeRecord == null) {
183
+ return null
184
+ }
183
185
  return object : Shape {
184
186
  override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
185
187
  val path = pathFromShapeRecord(shapeRecord, size, density)
@@ -65,7 +65,9 @@ fun findChildSlotView(viewGroup: ViewGroup, slotName: String): SlotView? {
65
65
  inline fun <reified T> findChildOfType(viewGroup: ViewGroup): T? {
66
66
  for (index in 0..<viewGroup.size) {
67
67
  val child = viewGroup.getChildAt(index)
68
- if (child is T) return child
68
+ if (child is T) {
69
+ return child
70
+ }
69
71
  }
70
72
  return null
71
73
  }
@@ -83,7 +83,11 @@ fun FunctionalComposableScope.SnackbarHostContent(
83
83
  "long" -> SnackbarDuration.Long
84
84
  "indefinite" -> SnackbarDuration.Indefinite
85
85
  // M3 default: indefinite when there's an action label, else short.
86
- else -> if (options.actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite
86
+ else -> if (options.actionLabel == null) {
87
+ SnackbarDuration.Short
88
+ } else {
89
+ SnackbarDuration.Indefinite
90
+ }
87
91
  }
88
92
  val result = try {
89
93
  withContext(scope.coroutineContext) {
@@ -134,7 +134,9 @@ enum class TextLineBreakType(val value: String) : Enumerable {
134
134
  }
135
135
 
136
136
  fun resolveFontFamily(name: String?, context: Context): FontFamily? {
137
- if (name == null) return null
137
+ if (name == null) {
138
+ return null
139
+ }
138
140
  return when (name) {
139
141
  "default" -> FontFamily.Default
140
142
  "sansSerif" -> FontFamily.SansSerif
@@ -32,7 +32,9 @@ val Color?.composeOrNull: androidx.compose.ui.graphics.Color?
32
32
  * Gets the ImageVector for a given icon name using reflection.
33
33
  */
34
34
  fun getImageVector(icon: String?): ImageVector? {
35
- if (icon.isNullOrEmpty()) return null
35
+ if (icon.isNullOrEmpty()) {
36
+ return null
37
+ }
36
38
  return try {
37
39
  val (theme, name) = icon.split(".")
38
40
  val clazz = Class.forName("androidx.compose.material.icons.$theme.${name}Kt")