@expo/ui 56.0.8 → 56.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 (252) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/CLAUDE.md +1 -1
  3. package/android/build.gradle +2 -2
  4. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +49 -6
  5. package/android/src/main/java/expo/modules/ui/HorizontalPagerView.kt +97 -16
  6. package/android/src/main/java/expo/modules/ui/LoadingView.kt +80 -0
  7. package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +31 -3
  8. package/android/src/main/java/expo/modules/ui/SnackbarView.kt +126 -0
  9. package/android/src/main/java/expo/modules/ui/colors/MaterialColors.kt +2 -0
  10. package/android/src/main/java/expo/modules/ui/state/ObservableState.kt +10 -0
  11. package/assets/keyboard_arrow_down.xml +10 -0
  12. package/build/State/index.d.ts +6 -0
  13. package/build/State/index.d.ts.map +1 -0
  14. package/build/State/useNativeState.d.ts +32 -3
  15. package/build/State/useNativeState.d.ts.map +1 -1
  16. package/build/community/bottom-sheet/BottomSheet.ios.d.ts.map +1 -1
  17. package/build/community/pager-view/PagerView.android.d.ts +7 -0
  18. package/build/community/pager-view/PagerView.android.d.ts.map +1 -0
  19. package/build/community/pager-view/PagerView.d.ts +8 -0
  20. package/build/community/pager-view/PagerView.d.ts.map +1 -0
  21. package/build/community/pager-view/PagerView.ios.d.ts +15 -0
  22. package/build/community/pager-view/PagerView.ios.d.ts.map +1 -0
  23. package/build/community/pager-view/index.d.ts +3 -0
  24. package/build/community/pager-view/index.d.ts.map +1 -0
  25. package/build/community/pager-view/types.d.ts +128 -0
  26. package/build/community/pager-view/types.d.ts.map +1 -0
  27. package/build/community/segmented-control/vendor/SegmentsSeparators.d.ts.map +1 -1
  28. package/build/jetpack-compose/HorizontalPager/index.d.ts +27 -0
  29. package/build/jetpack-compose/HorizontalPager/index.d.ts.map +1 -1
  30. package/build/jetpack-compose/Host/index.d.ts +1 -0
  31. package/build/jetpack-compose/Host/index.d.ts.map +1 -1
  32. package/build/jetpack-compose/LoadingIndicator/index.d.ts +41 -0
  33. package/build/jetpack-compose/LoadingIndicator/index.d.ts.map +1 -0
  34. package/build/jetpack-compose/Snackbar/index.d.ts +94 -0
  35. package/build/jetpack-compose/Snackbar/index.d.ts.map +1 -0
  36. package/build/jetpack-compose/SyncSwitch/index.d.ts +1 -1
  37. package/build/jetpack-compose/SyncSwitch/index.d.ts.map +1 -1
  38. package/build/jetpack-compose/TextField/index.d.ts +1 -1
  39. package/build/jetpack-compose/TextField/index.d.ts.map +1 -1
  40. package/build/jetpack-compose/index.d.ts +3 -2
  41. package/build/jetpack-compose/index.d.ts.map +1 -1
  42. package/build/jetpack-compose/modifiers/index.d.ts +6 -2
  43. package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
  44. package/build/swift-ui/BottomSheet/index.d.ts +5 -1
  45. package/build/swift-ui/BottomSheet/index.d.ts.map +1 -1
  46. package/build/swift-ui/Host/index.d.ts +1 -0
  47. package/build/swift-ui/Host/index.d.ts.map +1 -1
  48. package/build/swift-ui/ScrollView/index.d.ts +30 -0
  49. package/build/swift-ui/ScrollView/index.d.ts.map +1 -1
  50. package/build/swift-ui/SecureField/index.d.ts +1 -1
  51. package/build/swift-ui/SecureField/index.d.ts.map +1 -1
  52. package/build/swift-ui/SyncToggle/index.d.ts +1 -1
  53. package/build/swift-ui/SyncToggle/index.d.ts.map +1 -1
  54. package/build/swift-ui/TextField/index.d.ts +1 -1
  55. package/build/swift-ui/TextField/index.d.ts.map +1 -1
  56. package/build/swift-ui/index.d.ts +2 -2
  57. package/build/swift-ui/index.d.ts.map +1 -1
  58. package/build/swift-ui/modifiers/index.d.ts +25 -15
  59. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  60. package/build/swift-ui/modifiers/scrollObservation.d.ts +52 -0
  61. package/build/swift-ui/modifiers/scrollObservation.d.ts.map +1 -0
  62. package/build/swift-ui/modifiers/scrollPosition.d.ts +1 -1
  63. package/build/swift-ui/modifiers/scrollPosition.d.ts.map +1 -1
  64. package/build/swift-ui/modifiers/symbolEffect.d.ts +1 -1
  65. package/build/swift-ui/modifiers/symbolEffect.d.ts.map +1 -1
  66. package/build/swift-ui/withAnimation.d.ts +26 -0
  67. package/build/swift-ui/withAnimation.d.ts.map +1 -0
  68. package/build/universal/BottomSheet/index.android.d.ts +1 -1
  69. package/build/universal/BottomSheet/index.android.d.ts.map +1 -1
  70. package/build/universal/BottomSheet/index.d.ts +1 -1
  71. package/build/universal/BottomSheet/index.d.ts.map +1 -1
  72. package/build/universal/BottomSheet/index.ios.d.ts +1 -1
  73. package/build/universal/BottomSheet/index.ios.d.ts.map +1 -1
  74. package/build/universal/BottomSheet/types.d.ts +27 -0
  75. package/build/universal/BottomSheet/types.d.ts.map +1 -1
  76. package/build/universal/Checkbox/index.d.ts.map +1 -1
  77. package/build/universal/Collapsible/index.android.d.ts +8 -0
  78. package/build/universal/Collapsible/index.android.d.ts.map +1 -0
  79. package/build/universal/Collapsible/index.d.ts +8 -0
  80. package/build/universal/Collapsible/index.d.ts.map +1 -0
  81. package/build/universal/Collapsible/index.ios.d.ts +7 -0
  82. package/build/universal/Collapsible/index.ios.d.ts.map +1 -0
  83. package/build/universal/Collapsible/types.d.ts +23 -0
  84. package/build/universal/Collapsible/types.d.ts.map +1 -0
  85. package/build/universal/Column/index.d.ts.map +1 -1
  86. package/build/universal/Host/index.d.ts +5 -18
  87. package/build/universal/Host/index.d.ts.map +1 -1
  88. package/build/universal/Host/types.d.ts +72 -0
  89. package/build/universal/Host/types.d.ts.map +1 -0
  90. package/build/universal/List/index.android.d.ts +9 -0
  91. package/build/universal/List/index.android.d.ts.map +1 -0
  92. package/build/universal/List/index.d.ts +8 -0
  93. package/build/universal/List/index.d.ts.map +1 -0
  94. package/build/universal/List/index.ios.d.ts +8 -0
  95. package/build/universal/List/index.ios.d.ts.map +1 -0
  96. package/build/universal/List/types.d.ts +26 -0
  97. package/build/universal/List/types.d.ts.map +1 -0
  98. package/build/universal/ListItem/ListItem.android.d.ts +8 -0
  99. package/build/universal/ListItem/ListItem.android.d.ts.map +1 -0
  100. package/build/universal/ListItem/ListItem.d.ts +9 -0
  101. package/build/universal/ListItem/ListItem.d.ts.map +1 -0
  102. package/build/universal/ListItem/ListItem.ios.d.ts +8 -0
  103. package/build/universal/ListItem/ListItem.ios.d.ts.map +1 -0
  104. package/build/universal/ListItem/ListItemSlots.d.ts +21 -0
  105. package/build/universal/ListItem/ListItemSlots.d.ts.map +1 -0
  106. package/build/universal/ListItem/index.d.ts +10 -0
  107. package/build/universal/ListItem/index.d.ts.map +1 -0
  108. package/build/universal/ListItem/types.d.ts +59 -0
  109. package/build/universal/ListItem/types.d.ts.map +1 -0
  110. package/build/universal/Picker/Picker.android.d.ts +9 -0
  111. package/build/universal/Picker/Picker.android.d.ts.map +1 -0
  112. package/build/universal/Picker/Picker.d.ts +8 -0
  113. package/build/universal/Picker/Picker.d.ts.map +1 -0
  114. package/build/universal/Picker/Picker.ios.d.ts +9 -0
  115. package/build/universal/Picker/Picker.ios.d.ts.map +1 -0
  116. package/build/universal/Picker/PickerItem.d.ts +9 -0
  117. package/build/universal/Picker/PickerItem.d.ts.map +1 -0
  118. package/build/universal/Picker/index.d.ts +8 -0
  119. package/build/universal/Picker/index.d.ts.map +1 -0
  120. package/build/universal/Picker/types.d.ts +69 -0
  121. package/build/universal/Picker/types.d.ts.map +1 -0
  122. package/build/universal/RNHostView/index.android.d.ts +7 -0
  123. package/build/universal/RNHostView/index.android.d.ts.map +1 -0
  124. package/build/universal/RNHostView/index.d.ts +7 -0
  125. package/build/universal/RNHostView/index.d.ts.map +1 -0
  126. package/build/universal/RNHostView/index.ios.d.ts +7 -0
  127. package/build/universal/RNHostView/index.ios.d.ts.map +1 -0
  128. package/build/universal/RNHostView/types.d.ts +23 -0
  129. package/build/universal/RNHostView/types.d.ts.map +1 -0
  130. package/build/universal/Switch/index.d.ts.map +1 -1
  131. package/build/universal/TextInput/index.d.ts.map +1 -1
  132. package/build/universal/index.d.ts +5 -0
  133. package/build/universal/index.d.ts.map +1 -1
  134. package/expo-module.config.json +1 -1
  135. package/ios/BottomSheetView.swift +4 -1
  136. package/ios/ExpoUIModule.swift +47 -4
  137. package/ios/HostView.swift +21 -18
  138. package/ios/Modifiers/AnimationConfig.swift +109 -0
  139. package/ios/Modifiers/FontModifier.swift +72 -20
  140. package/ios/Modifiers/ScrollObservationModifiers.swift +107 -0
  141. package/ios/Modifiers/ViewModifierRegistry.swift +9 -112
  142. package/ios/State/ObservableState.swift +12 -1
  143. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8-sources.jar → 56.0.10/expo.modules.ui-56.0.10-sources.jar} +0 -0
  144. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.md5 +1 -0
  145. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.sha1 +1 -0
  146. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.sha256 +1 -0
  147. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10-sources.jar.sha512 +1 -0
  148. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar +0 -0
  149. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.md5 +1 -0
  150. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.sha1 +1 -0
  151. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.sha256 +1 -0
  152. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.aar.sha512 +1 -0
  153. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8.module → 56.0.10/expo.modules.ui-56.0.10.module} +22 -22
  154. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.md5 +1 -0
  155. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.sha1 +1 -0
  156. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.sha256 +1 -0
  157. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.module.sha512 +1 -0
  158. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8.pom → 56.0.10/expo.modules.ui-56.0.10.pom} +1 -1
  159. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.md5 +1 -0
  160. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.sha1 +1 -0
  161. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.sha256 +1 -0
  162. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.10/expo.modules.ui-56.0.10.pom.sha512 +1 -0
  163. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  164. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  165. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  166. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  167. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  168. package/package.json +7 -3
  169. package/src/State/index.ts +10 -0
  170. package/src/State/useNativeState.ts +71 -3
  171. package/src/community/bottom-sheet/BottomSheet.ios.tsx +0 -17
  172. package/src/community/pager-view/PagerView.android.tsx +223 -0
  173. package/src/community/pager-view/PagerView.ios.tsx +267 -0
  174. package/src/community/pager-view/PagerView.tsx +14 -0
  175. package/src/community/pager-view/index.tsx +13 -0
  176. package/src/community/pager-view/types.tsx +137 -0
  177. package/src/community/picker/Picker.android.tsx +1 -1
  178. package/src/community/segmented-control/vendor/SegmentsSeparators.tsx +3 -6
  179. package/src/jetpack-compose/HorizontalPager/index.tsx +89 -18
  180. package/src/jetpack-compose/Host/index.tsx +1 -0
  181. package/src/jetpack-compose/LoadingIndicator/index.tsx +91 -0
  182. package/src/jetpack-compose/Snackbar/index.tsx +135 -0
  183. package/src/jetpack-compose/SyncSwitch/index.tsx +1 -3
  184. package/src/jetpack-compose/TextField/index.tsx +1 -4
  185. package/src/jetpack-compose/index.ts +3 -2
  186. package/src/jetpack-compose/modifiers/index.ts +5 -2
  187. package/src/swift-ui/BottomSheet/index.tsx +32 -15
  188. package/src/swift-ui/Host/index.tsx +1 -0
  189. package/src/swift-ui/ScrollView/index.tsx +33 -0
  190. package/src/swift-ui/SecureField/index.tsx +1 -4
  191. package/src/swift-ui/SyncToggle/index.tsx +1 -3
  192. package/src/swift-ui/TextField/index.tsx +1 -4
  193. package/src/swift-ui/index.tsx +2 -3
  194. package/src/swift-ui/modifiers/index.ts +37 -14
  195. package/src/swift-ui/modifiers/scrollObservation.ts +80 -0
  196. package/src/swift-ui/modifiers/scrollPosition.ts +1 -2
  197. package/src/swift-ui/modifiers/symbolEffect.ts +1 -2
  198. package/src/swift-ui/withAnimation.ts +71 -0
  199. package/src/ts-declarations/react-native-web.d.ts +7 -0
  200. package/src/universal/BottomSheet/index.android.tsx +58 -11
  201. package/src/universal/BottomSheet/index.ios.tsx +40 -20
  202. package/src/universal/BottomSheet/index.tsx +49 -4
  203. package/src/universal/BottomSheet/types.ts +25 -0
  204. package/src/universal/Checkbox/index.tsx +14 -2
  205. package/src/universal/Collapsible/index.android.tsx +72 -0
  206. package/src/universal/Collapsible/index.ios.tsx +16 -0
  207. package/src/universal/Collapsible/index.tsx +71 -0
  208. package/src/universal/Collapsible/types.ts +25 -0
  209. package/src/universal/Column/index.tsx +3 -1
  210. package/src/universal/Host/index.tsx +9 -10
  211. package/src/universal/Host/types.ts +70 -0
  212. package/src/universal/List/index.android.tsx +44 -0
  213. package/src/universal/List/index.ios.tsx +19 -0
  214. package/src/universal/List/index.tsx +26 -0
  215. package/src/universal/List/types.ts +28 -0
  216. package/src/universal/ListItem/ListItem.android.tsx +52 -0
  217. package/src/universal/ListItem/ListItem.ios.tsx +58 -0
  218. package/src/universal/ListItem/ListItem.tsx +77 -0
  219. package/src/universal/ListItem/ListItemSlots.tsx +66 -0
  220. package/src/universal/ListItem/index.ts +15 -0
  221. package/src/universal/ListItem/types.ts +67 -0
  222. package/src/universal/Picker/Picker.android.tsx +69 -0
  223. package/src/universal/Picker/Picker.ios.tsx +45 -0
  224. package/src/universal/Picker/Picker.tsx +52 -0
  225. package/src/universal/Picker/PickerItem.tsx +27 -0
  226. package/src/universal/Picker/index.ts +11 -0
  227. package/src/universal/Picker/types.ts +79 -0
  228. package/src/universal/RNHostView/index.android.tsx +33 -0
  229. package/src/universal/RNHostView/index.ios.tsx +12 -0
  230. package/src/universal/RNHostView/index.tsx +39 -0
  231. package/src/universal/RNHostView/types.ts +25 -0
  232. package/src/universal/Switch/index.tsx +7 -2
  233. package/src/universal/TextInput/index.tsx +12 -2
  234. package/src/universal/index.ts +5 -0
  235. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.md5 +0 -1
  236. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha1 +0 -1
  237. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha256 +0 -1
  238. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha512 +0 -1
  239. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar +0 -0
  240. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.md5 +0 -1
  241. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha1 +0 -1
  242. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha256 +0 -1
  243. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha512 +0 -1
  244. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.md5 +0 -1
  245. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha1 +0 -1
  246. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha256 +0 -1
  247. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha512 +0 -1
  248. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.md5 +0 -1
  249. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha1 +0 -1
  250. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha256 +0 -1
  251. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha512 +0 -1
  252. package/src/community/bottom-sheet/CLAUDE.md +0 -55
@@ -1,7 +1,6 @@
1
1
  import type { UnitPointValue } from '.';
2
2
  import { createModifier, createModifierWithEventListener } from './createModifier';
3
- import { type ObservableState } from '../../State/useNativeState';
4
- import { getStateId } from '../../State/utils';
3
+ import { getStateId, type ObservableState } from '../../State';
5
4
 
6
5
  /**
7
6
  * Attaches a stable identifier to a view so it can be referenced by scroll target bindings.
@@ -1,6 +1,5 @@
1
1
  import { createModifier } from './createModifier';
2
- import { type ObservableState } from '../../State/useNativeState';
3
- import { getStateId } from '../../State/utils';
2
+ import { getStateId, type ObservableState } from '../../State';
4
3
 
5
4
  // https://developer.apple.com/documentation/symbols/appearsymboleffect
6
5
  type AppearSymbolEffect = {
@@ -0,0 +1,71 @@
1
+ import { requireNativeModule } from 'expo';
2
+
3
+ import { worklets } from '../State';
4
+ import { VALUE_SYMBOL } from './modifiers/animation/constants';
5
+ import type { AnimationObject, ChainableAnimationType } from './modifiers/animation/types';
6
+
7
+ const ExpoUI = requireNativeModule('ExpoUI');
8
+
9
+ /**
10
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/animationcompletioncriteria).
11
+ */
12
+ export type WithAnimationCompletionCriteria = 'logicallyComplete' | 'removed';
13
+
14
+ /**
15
+ * Mirrors SwiftUI's [`withAnimation(_:_:)`](https://developer.apple.com/documentation/swiftui/withanimation(_:_:)).
16
+ * The body must be a worklet so the mutations run synchronously on the
17
+ * UI thread inside the animation transaction.
18
+ *
19
+ * Performs `body` inside a SwiftUI animation transaction. Any
20
+ * `useNativeState` values mutated by the worklet animate to their new value
21
+ * using `animation`.
22
+ *
23
+ * @param animation Animation to apply, built with the `Animation` factory
24
+ * from `@expo/ui/swift-ui/modifiers`. Pass `null` to run `body` without an
25
+ * animation.
26
+ * @param body Worklet that mutates one or more `useNativeState` values.
27
+ * @param completion Optional worklet invoked on the main thread when the
28
+ * animation finishes. Requires iOS 17 / tvOS 17; on earlier versions the
29
+ * animation still runs but the callback is silently skipped.
30
+ * @param completionCriteria Controls when `completion` fires. Defaults to
31
+ * `'logicallyComplete'`.
32
+ */
33
+ export function withAnimation(
34
+ animation: ChainableAnimationType | null,
35
+ body: () => void,
36
+ completion?: () => void,
37
+ completionCriteria?: WithAnimationCompletionCriteria
38
+ ): void {
39
+ if (!worklets) {
40
+ throw new Error(
41
+ "withAnimation needs the 'react-native-worklets' package, which couldn't be loaded. " +
42
+ 'Install react-native-worklets and rebuild the native app, then call withAnimation again.'
43
+ );
44
+ }
45
+
46
+ if (!worklets.isWorkletFunction(body)) {
47
+ throw new Error(
48
+ 'withAnimation body must be a worklet. Worklets run synchronously on the UI thread ' +
49
+ "so state changes are captured by SwiftUI's animation transaction. " +
50
+ "Add the 'worklet' directive as the first statement: " +
51
+ "() => { 'worklet'; state.value = next; }."
52
+ );
53
+ }
54
+
55
+ let completionCallback: object | null = null;
56
+ if (completion) {
57
+ if (!worklets.isWorkletFunction(completion)) {
58
+ throw new Error(
59
+ "withAnimation completion must be a worklet. Add the 'worklet' directive as the " +
60
+ 'first statement in your completion callback.'
61
+ );
62
+ }
63
+ completionCallback = new ExpoUI.WorkletCallback(worklets.createSerializable(completion));
64
+ }
65
+
66
+ const animationObject: AnimationObject | null =
67
+ animation == null ? null : animation[VALUE_SYMBOL]();
68
+
69
+ const bodyCallback = new ExpoUI.WorkletCallback(worklets.createSerializable(body));
70
+ ExpoUI.withAnimation(animationObject, bodyCallback, completionCallback, completionCriteria);
71
+ }
@@ -7,6 +7,7 @@ declare module 'react-native' {
7
7
  ) => React.ReactElement<P>;
8
8
 
9
9
  type DisplayValue = ReactNative.FlexStyle['display'] | 'inline-flex';
10
+ type OverflowValue = 'auto' | 'hidden' | 'scroll' | 'visible';
10
11
  type WebDimensionValue = ReactNative.DimensionValue | string;
11
12
 
12
13
  type WebRole =
@@ -104,6 +105,8 @@ declare module 'react-native' {
104
105
  display?: DisplayValue;
105
106
  height?: WebDimensionValue;
106
107
  width?: WebDimensionValue;
108
+ overflowX?: OverflowValue;
109
+ overflowY?: OverflowValue;
107
110
  paddingLeft?: WebDimensionValue;
108
111
  paddingRight?: WebDimensionValue;
109
112
  paddingTop?: WebDimensionValue;
@@ -114,6 +117,8 @@ declare module 'react-native' {
114
117
  display?: DisplayValue;
115
118
  height?: WebDimensionValue;
116
119
  width?: WebDimensionValue;
120
+ overflowX?: OverflowValue;
121
+ overflowY?: OverflowValue;
117
122
  paddingLeft?: WebDimensionValue;
118
123
  paddingRight?: WebDimensionValue;
119
124
  paddingTop?: WebDimensionValue;
@@ -124,6 +129,8 @@ declare module 'react-native' {
124
129
  display?: DisplayValue;
125
130
  height?: WebDimensionValue;
126
131
  width?: WebDimensionValue;
132
+ overflowX?: OverflowValue;
133
+ overflowY?: OverflowValue;
127
134
  paddingLeft?: WebDimensionValue;
128
135
  paddingRight?: WebDimensionValue;
129
136
  paddingTop?: WebDimensionValue;
@@ -1,34 +1,81 @@
1
- import { Column, ModalBottomSheet } from '@expo/ui/jetpack-compose';
1
+ import { Column, Host, ModalBottomSheet, type ModalBottomSheetRef } from '@expo/ui/jetpack-compose';
2
2
  import {
3
+ fillMaxHeight,
3
4
  padding,
4
5
  testID as testIDModifier,
5
6
  type ModifierConfig,
6
7
  } from '@expo/ui/jetpack-compose/modifiers';
8
+ import { useEffect, useRef, useState } from 'react';
7
9
 
8
- import type { BottomSheetProps } from './types';
10
+ import type { BottomSheetProps, SnapPoint } from './types';
11
+
12
+ // M3 `ModalBottomSheet` only has partial/expanded states.
13
+ // Only allow the partial state when the consumer requested a partial-friendly snap point.
14
+ function shouldSkipPartiallyExpanded(snapPoints: SnapPoint[] | undefined): boolean {
15
+ if (!snapPoints || snapPoints.length === 0) return false;
16
+ return !snapPoints.some(
17
+ (sp) =>
18
+ sp === 'half' ||
19
+ (typeof sp === 'object' && 'fraction' in sp && sp.fraction < 1) ||
20
+ (typeof sp === 'object' && 'height' in sp)
21
+ );
22
+ }
23
+
24
+ // M3 sizes content to intrinsic height.
25
+ // Apply `fillMaxHeight` so `'full'` actually fills the viewport instead of stopping at content height.
26
+ function shouldFillMaxHeight(snapPoints: SnapPoint[] | undefined): boolean {
27
+ if (!snapPoints || snapPoints.length === 0) return false;
28
+ return snapPoints.some(
29
+ (sp) => sp === 'full' || (typeof sp === 'object' && 'fraction' in sp && sp.fraction >= 1)
30
+ );
31
+ }
9
32
 
10
33
  export function BottomSheet({
11
34
  children,
12
35
  isPresented,
13
36
  onDismiss,
14
37
  showDragIndicator = true,
38
+ snapPoints,
15
39
  testID,
16
40
  modifiers,
17
41
  }: BottomSheetProps) {
18
- if (!isPresented) return null;
42
+ const sheetRef = useRef<ModalBottomSheetRef>(null);
43
+ const [mount, setMount] = useState(isPresented);
44
+
45
+ useEffect(() => {
46
+ if (isPresented) {
47
+ setMount(true);
48
+ return;
49
+ }
50
+ let cancelled = false;
51
+ sheetRef.current?.hide().then(() => {
52
+ if (!cancelled) setMount(false);
53
+ });
54
+ return () => {
55
+ cancelled = true;
56
+ };
57
+ }, [isPresented]);
58
+
59
+ if (!mount) {
60
+ return null;
61
+ }
19
62
 
20
63
  const contentModifiers: ModifierConfig[] = [padding(16, showDragIndicator ? 0 : 16, 16, 0)];
64
+ if (shouldFillMaxHeight(snapPoints)) contentModifiers.push(fillMaxHeight());
21
65
  if (testID) contentModifiers.push(testIDModifier(testID));
22
66
 
23
67
  return (
24
- <ModalBottomSheet
25
- onDismissRequest={onDismiss}
26
- showDragHandle={showDragIndicator}
27
- modifiers={modifiers}>
28
- {/* When the drag handle is hidden, add top padding so content doesn't
29
- crop against the top edge of the sheet. */}
30
- <Column modifiers={contentModifiers}>{children}</Column>
31
- </ModalBottomSheet>
68
+ <Host style={{ position: 'absolute' }} pointerEvents="none">
69
+ <ModalBottomSheet
70
+ ref={sheetRef}
71
+ onDismissRequest={onDismiss}
72
+ showDragHandle={showDragIndicator}
73
+ skipPartiallyExpanded={shouldSkipPartiallyExpanded(snapPoints)}
74
+ modifiers={modifiers}>
75
+ {/* When the drag handle is hidden, add top padding so content doesn't crop against the top edge of the sheet. */}
76
+ <Column modifiers={contentModifiers}>{children}</Column>
77
+ </ModalBottomSheet>
78
+ </Host>
32
79
  );
33
80
  }
34
81
 
@@ -1,34 +1,54 @@
1
- import { BottomSheet as SwiftUIBottomSheet, Group } from '@expo/ui/swift-ui';
2
- import { frame, padding, presentationDragIndicator } from '@expo/ui/swift-ui/modifiers';
1
+ import { BottomSheet as SwiftUIBottomSheet, Group, Host } from '@expo/ui/swift-ui';
2
+ import {
3
+ frame,
4
+ padding,
5
+ presentationDetents,
6
+ presentationDragIndicator,
7
+ type ModifierConfig,
8
+ type PresentationDetent,
9
+ } from '@expo/ui/swift-ui/modifiers';
3
10
 
4
- import type { BottomSheetProps } from './types';
11
+ import type { BottomSheetProps, SnapPoint } from './types';
12
+
13
+ function snapPointToDetent(snapPoint: SnapPoint): PresentationDetent {
14
+ if (snapPoint === 'half') return 'medium';
15
+ if (snapPoint === 'full') return 'large';
16
+ return snapPoint;
17
+ }
5
18
 
6
19
  export function BottomSheet({
7
20
  children,
8
21
  isPresented,
9
22
  onDismiss,
10
23
  showDragIndicator = true,
24
+ snapPoints,
11
25
  testID,
12
26
  modifiers,
13
27
  }: BottomSheetProps) {
28
+ const presentationModifiers: ModifierConfig[] = [
29
+ frame({ maxWidth: Infinity, alignment: 'topLeading' }),
30
+ padding({ top: 16, leading: 16, trailing: 16 }),
31
+ presentationDragIndicator(showDragIndicator ? 'visible' : 'hidden'),
32
+ ];
33
+ if (snapPoints && snapPoints.length > 0) {
34
+ presentationModifiers.push(presentationDetents(snapPoints.map(snapPointToDetent)));
35
+ }
36
+ if (modifiers?.length) {
37
+ presentationModifiers.push(...modifiers);
38
+ }
39
+
14
40
  return (
15
- <SwiftUIBottomSheet
16
- isPresented={isPresented}
17
- onIsPresentedChange={(presented) => {
18
- if (!presented) onDismiss();
19
- }}
20
- fitToContents
21
- testID={testID}>
22
- <Group
23
- modifiers={[
24
- frame({ maxWidth: Infinity, alignment: 'topLeading' }),
25
- padding({ top: 16, leading: 16, trailing: 16 }),
26
- presentationDragIndicator(showDragIndicator ? 'visible' : 'hidden'),
27
- ...(modifiers ?? []),
28
- ]}>
29
- {children}
30
- </Group>
31
- </SwiftUIBottomSheet>
41
+ <Host style={{ position: 'absolute' }} pointerEvents="none">
42
+ <SwiftUIBottomSheet
43
+ isPresented={isPresented}
44
+ onIsPresentedChange={(presented) => {
45
+ if (!presented) onDismiss();
46
+ }}
47
+ fitToContents={!snapPoints || snapPoints.length === 0}
48
+ testID={testID}>
49
+ <Group modifiers={presentationModifiers}>{children}</Group>
50
+ </SwiftUIBottomSheet>
51
+ </Host>
32
52
  );
33
53
  }
34
54
 
@@ -1,6 +1,27 @@
1
+ import { useColorScheme } from 'react-native';
1
2
  import { Drawer } from 'vaul';
2
3
 
3
- import type { BottomSheetProps } from './types';
4
+ import type { BottomSheetProps, SnapPoint } from './types';
5
+
6
+ // Visually-hidden style for the screen-reader-only Drawer.Title.
7
+ const visuallyHiddenStyle: React.CSSProperties = {
8
+ position: 'absolute',
9
+ width: 1,
10
+ height: 1,
11
+ padding: 0,
12
+ margin: -1,
13
+ overflow: 'hidden',
14
+ clip: 'rect(0, 0, 0, 0)',
15
+ whiteSpace: 'nowrap',
16
+ border: 0,
17
+ };
18
+
19
+ function snapPointToVaul(snapPoint: SnapPoint): string | number {
20
+ if (snapPoint === 'half') return 0.5;
21
+ if (snapPoint === 'full') return 1;
22
+ if ('fraction' in snapPoint) return snapPoint.fraction;
23
+ return `${snapPoint.height}px`;
24
+ }
4
25
 
5
26
  /**
6
27
  * A modal sheet that slides up from the bottom of the screen.
@@ -10,17 +31,34 @@ export function BottomSheet({
10
31
  isPresented,
11
32
  onDismiss,
12
33
  showDragIndicator = true,
34
+ snapPoints,
13
35
  testID,
14
36
  }: BottomSheetProps) {
37
+ const isDark = useColorScheme() === 'dark';
38
+ const vaulSnapPoints = snapPoints?.length ? snapPoints.map(snapPointToVaul) : undefined;
39
+ const hasSnapPoints = vaulSnapPoints != null;
40
+
15
41
  return (
16
42
  <Drawer.Root
17
43
  open={isPresented}
18
44
  onOpenChange={(open) => {
19
45
  if (!open) onDismiss();
20
- }}>
46
+ }}
47
+ snapPoints={vaulSnapPoints}>
21
48
  <Drawer.Portal>
22
49
  <Drawer.Overlay style={overlayStyle} />
23
- <Drawer.Content style={{ ...contentStyle, ...(showDragIndicator && dragIndicatorSpacing) }}>
50
+ <Drawer.Content
51
+ style={{
52
+ ...contentStyle,
53
+ ...(isDark && { backgroundColor: '#000' }),
54
+ // Snap-points mode: vaul translates the drawer by `viewport - snapHeight`.
55
+ // The drawer has to fill the viewport or it gets pushed off-screen.
56
+ ...(hasSnapPoints ? snapPointContentStyle : noSnapPointContentStyle),
57
+ ...(showDragIndicator && dragIndicatorSpacing),
58
+ }}
59
+ aria-describedby={undefined}>
60
+ {/* Radix Dialog requires a title for a11y; render visually-hidden. */}
61
+ <Drawer.Title style={visuallyHiddenStyle}>Bottom sheet</Drawer.Title>
24
62
  {showDragIndicator && <Drawer.Handle />}
25
63
  <div style={innerStyle} data-testid={testID}>
26
64
  {children}
@@ -47,12 +85,19 @@ const contentStyle: React.CSSProperties = {
47
85
  borderTopLeftRadius: 16,
48
86
  borderTopRightRadius: 16,
49
87
  zIndex: 50,
50
- maxHeight: '85vh',
51
88
  outline: 'none',
52
89
  display: 'flex',
53
90
  flexDirection: 'column',
54
91
  };
55
92
 
93
+ const snapPointContentStyle: React.CSSProperties = {
94
+ height: '96vh',
95
+ };
96
+
97
+ const noSnapPointContentStyle: React.CSSProperties = {
98
+ maxHeight: '85vh',
99
+ };
100
+
56
101
  const innerStyle: React.CSSProperties = {
57
102
  padding: 16,
58
103
  overflow: 'auto',
@@ -1,5 +1,20 @@
1
1
  import type { ModifierConfig } from '../../types';
2
2
 
3
+ /**
4
+ * A snap point describing one of the heights a [`BottomSheet`](#bottomsheet) can rest at.
5
+ *
6
+ * - `'half'` — Approximately half-screen.
7
+ * - `'full'` — Fully expanded.
8
+ * - `{ fraction }` — A fraction of the screen height (0–1).
9
+ * iOS / web only.
10
+ * - `{ height }` — A fixed pixel height.
11
+ * iOS / web only.
12
+ *
13
+ * On Android, `{ fraction }` and `{ height }` snap to the nearest of `'half'` / `'full'`.
14
+ * See the component docs for platform behavior notes.
15
+ */
16
+ export type SnapPoint = 'half' | 'full' | { fraction: number } | { height: number };
17
+
3
18
  /**
4
19
  * Props for the [`BottomSheet`](#bottomsheet) component, a modal sheet that slides up from the bottom of the screen.
5
20
  */
@@ -25,6 +40,16 @@ export interface BottomSheetProps {
25
40
  */
26
41
  showDragIndicator?: boolean;
27
42
 
43
+ /**
44
+ * Heights the sheet can rest at.
45
+ * When omitted, the sheet auto-sizes to its content.
46
+ * See [`SnapPoint`](#snappoint) for the supported values.
47
+ *
48
+ * @example `['half', 'full']` — draggable between half and full
49
+ * @example `['full']` — always full height
50
+ */
51
+ snapPoints?: SnapPoint[];
52
+
28
53
  /**
29
54
  * Identifier used to locate the component in end-to-end tests.
30
55
  */
@@ -1,5 +1,12 @@
1
1
  import type { ComponentProps } from 'react';
2
- import { StyleSheet, Text, unstable_createElement, View, type ViewProps } from 'react-native';
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ unstable_createElement,
6
+ useColorScheme,
7
+ View,
8
+ type ViewProps,
9
+ } from 'react-native';
3
10
 
4
11
  import type { CheckboxProps } from './types';
5
12
 
@@ -19,6 +26,9 @@ const styles = StyleSheet.create({
19
26
  // @ts-expect-error
20
27
  cursor: 'inherit',
21
28
  },
29
+ darkText: {
30
+ color: '#fff',
31
+ },
22
32
  });
23
33
 
24
34
  const NativeCheckbox = (
@@ -29,6 +39,8 @@ const NativeCheckbox = (
29
39
  * A toggle control that represents a checked or unchecked state.
30
40
  */
31
41
  export function Checkbox({ value, onValueChange, label, disabled = false, testID }: CheckboxProps) {
42
+ const isDark = useColorScheme() === 'dark';
43
+
32
44
  return (
33
45
  <View role="label" aria-disabled={disabled} style={[styles.label, disabled && styles.disabled]}>
34
46
  <NativeCheckbox
@@ -38,7 +50,7 @@ export function Checkbox({ value, onValueChange, label, disabled = false, testID
38
50
  data-testid={testID}
39
51
  style={styles.cursorInherit}
40
52
  />
41
- {label != null && <Text>{label}</Text>}
53
+ {label != null && <Text style={isDark && styles.darkText}>{label}</Text>}
42
54
  </View>
43
55
  );
44
56
  }
@@ -0,0 +1,72 @@
1
+ import {
2
+ AnimatedVisibility,
3
+ Column,
4
+ EnterTransition,
5
+ ExitTransition,
6
+ Icon,
7
+ ListItem,
8
+ Text,
9
+ useMaterialColors,
10
+ } from '@expo/ui/jetpack-compose';
11
+ import {
12
+ animated,
13
+ background,
14
+ clickable,
15
+ clip,
16
+ graphicsLayer,
17
+ padding,
18
+ Shapes,
19
+ spring,
20
+ } from '@expo/ui/jetpack-compose/modifiers';
21
+
22
+ import type { CollapsibleProps } from './types';
23
+
24
+ const KEYBOARD_ARROW_DOWN = require('../../../assets/keyboard_arrow_down.xml');
25
+
26
+ // M3 large-corner token (16dp) — the Expressive expandable-list-item pattern.
27
+ const CONTAINER_SHAPE = Shapes.RoundedCorner(16);
28
+
29
+ // expandVertically + fadeIn keeps motion strictly vertical.
30
+ // (Compose's default expandIn includes a horizontal component.)
31
+ const ENTER = EnterTransition.expandVertically().plus(EnterTransition.fadeIn());
32
+ const EXIT = ExitTransition.shrinkVertically().plus(ExitTransition.fadeOut());
33
+
34
+ /**
35
+ * Android implementation of `Collapsible`.
36
+ * A rounded M3 card whose container tint fades between `transparent` (collapsed) and `surfaceContainer` (expanded).
37
+ */
38
+ export function Collapsible({ isOpen, onOpenChange, label = '', children }: CollapsibleProps) {
39
+ const colors = useMaterialColors();
40
+ const containerColor = isOpen ? colors.surfaceContainer : 'transparent';
41
+
42
+ return (
43
+ <Column
44
+ modifiers={[
45
+ // `clip` first so background paint and the inner ListItem's ripple both respect the rounded shape.
46
+ clip(CONTAINER_SHAPE),
47
+ background(containerColor, { animationSpec: spring() }),
48
+ ]}>
49
+ <ListItem
50
+ // Transparent so the outer `background` is the sole tint source.
51
+ colors={{ containerColor: 'transparent' }}
52
+ modifiers={[clickable(() => onOpenChange(!isOpen))]}>
53
+ <ListItem.HeadlineContent>
54
+ <Text>{label}</Text>
55
+ </ListItem.HeadlineContent>
56
+ <ListItem.TrailingContent>
57
+ <Icon
58
+ source={KEYBOARD_ARROW_DOWN}
59
+ modifiers={[graphicsLayer({ rotationZ: animated(isOpen ? 180 : 0, spring()) })]}
60
+ />
61
+ </ListItem.TrailingContent>
62
+ </ListItem>
63
+ <AnimatedVisibility visible={isOpen} enterTransition={ENTER} exitTransition={EXIT}>
64
+ {/* 16dp matches the M3 large-corner radius.
65
+ ListItem's own bottom padding adds the M3 header–body separation on top. */}
66
+ <Column modifiers={[padding(16, 16, 16, 16)]}>{children}</Column>
67
+ </AnimatedVisibility>
68
+ </Column>
69
+ );
70
+ }
71
+
72
+ export * from './types';
@@ -0,0 +1,16 @@
1
+ import { DisclosureGroup } from '@expo/ui/swift-ui';
2
+
3
+ import type { CollapsibleProps } from './types';
4
+
5
+ /**
6
+ * iOS implementation of `Collapsible`. Wraps SwiftUI's `DisclosureGroup`.
7
+ */
8
+ export function Collapsible({ isOpen, onOpenChange, label = '', children }: CollapsibleProps) {
9
+ return (
10
+ <DisclosureGroup label={label} isExpanded={isOpen} onIsExpandedChange={onOpenChange}>
11
+ {children}
12
+ </DisclosureGroup>
13
+ );
14
+ }
15
+
16
+ export * from './types';
@@ -0,0 +1,71 @@
1
+ import type { ComponentProps, SyntheticEvent } from 'react';
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ unstable_createElement,
6
+ useColorScheme,
7
+ View,
8
+ type TextProps,
9
+ type ViewProps,
10
+ } from 'react-native';
11
+
12
+ import type { CollapsibleProps } from './types';
13
+
14
+ const Details = (
15
+ props: Omit<ComponentProps<'details'>, 'style'> & { style?: TextProps['style'] }
16
+ ) => unstable_createElement('details', props);
17
+
18
+ const Summary = (
19
+ props: Omit<ComponentProps<'summary'>, 'style'> & { style?: ViewProps['style'] }
20
+ ) => unstable_createElement('summary', props);
21
+
22
+ /**
23
+ * A primitive that toggles visibility of its content via a labelled tappable
24
+ * header. Controlled via `isOpen` + `onOpenChange`.
25
+ */
26
+ export function Collapsible({ isOpen, onOpenChange, label = '', children }: CollapsibleProps) {
27
+ const isDark = useColorScheme() === 'dark';
28
+
29
+ return (
30
+ <Details
31
+ open={isOpen}
32
+ onToggle={(event: SyntheticEvent<HTMLDetailsElement>) => {
33
+ const nextOpen = event.currentTarget.open;
34
+ if (nextOpen !== isOpen) {
35
+ onOpenChange(nextOpen);
36
+ }
37
+ }}
38
+ style={[styles.container, isDark && styles.darkText]}>
39
+ <Summary style={styles.summary}>
40
+ <Text style={isDark && styles.darkText}>{label}</Text>
41
+ </Summary>
42
+ <View style={styles.content}>{children}</View>
43
+ </Details>
44
+ );
45
+ }
46
+
47
+ const styles = StyleSheet.create({
48
+ container: {
49
+ flexDirection: 'column',
50
+ width: '100%',
51
+ },
52
+ summary: {
53
+ flexDirection: 'row',
54
+ alignItems: 'center',
55
+ gap: 8,
56
+ width: '100%',
57
+ paddingHorizontal: 16,
58
+ paddingVertical: 12,
59
+ userSelect: 'none',
60
+ cursor: 'pointer',
61
+ },
62
+ content: {
63
+ paddingHorizontal: 16,
64
+ paddingBottom: 12,
65
+ },
66
+ darkText: {
67
+ color: '#fff',
68
+ },
69
+ });
70
+
71
+ export * from './types';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Props for the [`Collapsible`](#collapsible) component, a primitive that
3
+ * shows or hides its content with a tap on a labelled header.
4
+ */
5
+ export interface CollapsibleProps {
6
+ /**
7
+ * Whether the content is currently expanded.
8
+ */
9
+ isOpen: boolean;
10
+
11
+ /**
12
+ * Called when the user taps the header to toggle the open state.
13
+ */
14
+ onOpenChange: (isOpen: boolean) => void;
15
+
16
+ /**
17
+ * Text rendered in the tappable header.
18
+ */
19
+ label?: string;
20
+
21
+ /**
22
+ * Content rendered when `isOpen` is `true`.
23
+ */
24
+ children?: React.ReactNode;
25
+ }
@@ -5,7 +5,9 @@ import { useUniversalLifecycle } from '../hooks';
5
5
  import type { UniversalAlignment } from '../types';
6
6
 
7
7
  const styles = StyleSheet.create({
8
- column: { flexDirection: 'column' },
8
+ // `alignSelf: 'stretch'` — match SwiftUI `VStack` / Compose `Column`, which fill their parent's cross-axis.
9
+ // Without this, a nested Column inherits its parent's `alignItems` (often `flex-start`) and shrinks to content.
10
+ column: { flexDirection: 'column', alignSelf: 'stretch' },
9
11
  hidden: { display: 'none' },
10
12
  disabled: {
11
13
  opacity: 0.5,