@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
@@ -0,0 +1,91 @@
1
+ import { requireNativeView } from 'expo';
2
+ import { type ColorValue } from 'react-native';
3
+
4
+ import { getStateId, type ObservableState } from '../../State';
5
+ import { type ModifierConfig } from '../../types';
6
+ import { createViewModifierEventListener } from '../modifiers/utils';
7
+
8
+ /**
9
+ * Common props shared by loading indicator variants.
10
+ */
11
+ export type LoadingIndicatorCommonConfig = {
12
+ /**
13
+ * An observable state that holds the current progress value.
14
+ * Create one with `useNativeState(0)`. Omit for indeterminate loading.
15
+ */
16
+ progress?: ObservableState<number | null>;
17
+ /**
18
+ * Loading indicator color.
19
+ */
20
+ color?: ColorValue;
21
+ /**
22
+ * Modifiers for the component.
23
+ */
24
+ modifiers?: ModifierConfig[];
25
+ };
26
+
27
+ type NativeLoadingIndicatorCommonConfig = Omit<
28
+ LoadingIndicatorCommonConfig,
29
+ 'progress' | 'modifiers'
30
+ > & {
31
+ progress?: number;
32
+ modifiers?: unknown;
33
+ };
34
+
35
+ function transformProps<T extends LoadingIndicatorCommonConfig>(
36
+ props: T
37
+ ): NativeLoadingIndicatorCommonConfig {
38
+ const { modifiers, progress, ...restProps } = props;
39
+ return {
40
+ modifiers,
41
+ ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
42
+ ...restProps,
43
+ progress: getStateId(progress),
44
+ };
45
+ }
46
+
47
+ function createLoadingIndicatorComponent<P extends LoadingIndicatorCommonConfig>(
48
+ viewName: string
49
+ ): React.ComponentType<P> {
50
+ const NativeView: React.ComponentType<NativeLoadingIndicatorCommonConfig> = requireNativeView(
51
+ 'ExpoUI',
52
+ viewName
53
+ );
54
+ function Component(props: P) {
55
+ return <NativeView {...transformProps(props)} />;
56
+ }
57
+ Component.displayName = viewName;
58
+ return Component;
59
+ }
60
+
61
+ // region LoadingIndicator
62
+
63
+ /**
64
+ * A loading indicator that displays loading using morphing shapes.
65
+ *
66
+ * Matches the Jetpack Compose `LoadingIndicator`.
67
+ */
68
+ export const LoadingIndicator = createLoadingIndicatorComponent('LoadingIndicatorView');
69
+
70
+ // endregion
71
+
72
+ // region ContainedLoadingIndicator
73
+
74
+ export type ContainedLoadingIndicatorProps = LoadingIndicatorCommonConfig & {
75
+ /**
76
+ * Loading indicator's container color
77
+ */
78
+ containerColor?: ColorValue;
79
+ };
80
+
81
+ /**
82
+ * A loading indicator that displays loading using morphing shapes inside a container.
83
+ *
84
+ * Matches the Jetpack Compose `ContainedLoadingIndicator`.
85
+ */
86
+ export const ContainedLoadingIndicator =
87
+ createLoadingIndicatorComponent<ContainedLoadingIndicatorProps>('ContainedLoadingIndicatorView');
88
+ // endregion
89
+
90
+ // Exported for docs api data
91
+ export { type ObservableState };
@@ -0,0 +1,135 @@
1
+ import { requireNativeView } from 'expo';
2
+ import { type Ref } from 'react';
3
+ import { type ColorValue } from 'react-native';
4
+
5
+ import { type ModifierConfig } from '../../types';
6
+ import { createViewModifierEventListener } from '../modifiers/utils';
7
+
8
+ export type SnackbarProps = {
9
+ /**
10
+ * The background color of the snackbar container.
11
+ */
12
+ containerColor?: ColorValue;
13
+ /**
14
+ * The preferred content color used for the message text.
15
+ */
16
+ contentColor?: ColorValue;
17
+ /**
18
+ * The content color used for the action button.
19
+ */
20
+ actionContentColor?: ColorValue;
21
+ /**
22
+ * The content color used for the dismiss-action icon button.
23
+ */
24
+ dismissActionContentColor?: ColorValue;
25
+ /**
26
+ * Whether the action should be placed on a new line below the message.
27
+ * Useful for long action labels.
28
+ * @default false
29
+ */
30
+ actionOnNewLine?: boolean;
31
+ /**
32
+ * Modifiers for the component.
33
+ */
34
+ modifiers?: ModifierConfig[];
35
+ };
36
+
37
+ const SnackbarNativeView: React.ComponentType<SnackbarProps> = requireNativeView(
38
+ 'ExpoUI',
39
+ 'SnackbarView'
40
+ );
41
+
42
+ /**
43
+ * Styling configuration for the snackbar shown by `SnackbarHost`. Pass as a
44
+ * child to override colors or place the action on a new line.
45
+ */
46
+ export function Snackbar(props: SnackbarProps) {
47
+ const { modifiers, ...rest } = props;
48
+ return (
49
+ <SnackbarNativeView
50
+ modifiers={modifiers}
51
+ {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
52
+ {...rest}
53
+ />
54
+ );
55
+ }
56
+
57
+ // --- SnackbarHost ---
58
+
59
+ /**
60
+ * How long the snackbar is shown. Mirrors Compose's `SnackbarDuration` enum.
61
+ */
62
+ export type SnackbarDuration = 'short' | 'long' | 'indefinite';
63
+
64
+ /**
65
+ * Reason a snackbar invocation resolved. Mirrors Compose's `SnackbarResult` enum.
66
+ */
67
+ export type SnackbarResult = 'actionPerformed' | 'dismissed';
68
+
69
+ export type SnackbarShowOptions = {
70
+ /**
71
+ * The message body of the snackbar.
72
+ */
73
+ message: string;
74
+ /**
75
+ * Label for the optional action button. When omitted, no action button is shown.
76
+ */
77
+ actionLabel?: string;
78
+ /**
79
+ * Whether to show a trailing close (X) icon button to dismiss the snackbar.
80
+ * @default false
81
+ */
82
+ withDismissAction?: boolean;
83
+ /**
84
+ * How long to show the snackbar. Defaults to `'short'` when an `actionLabel`
85
+ * is not provided, and `'indefinite'` when it is, matching Compose.
86
+ */
87
+ duration?: SnackbarDuration;
88
+ };
89
+
90
+ export type SnackbarHostRef = {
91
+ /**
92
+ * Shows a snackbar and resolves with `'actionPerformed'` when the user taps
93
+ * the action, or `'dismissed'` when it times out or the dismiss-action
94
+ * button is tapped. Subsequent calls queue and show after the current
95
+ * snackbar is dismissed.
96
+ */
97
+ showSnackbar: (options: SnackbarShowOptions) => Promise<SnackbarResult>;
98
+ };
99
+
100
+ export type SnackbarHostProps = {
101
+ /**
102
+ * Ref exposing the imperative `showSnackbar` method.
103
+ */
104
+ ref?: Ref<SnackbarHostRef>;
105
+ /**
106
+ * Modifiers for the component.
107
+ */
108
+ modifiers?: ModifierConfig[];
109
+ /**
110
+ * Optional `Snackbar` child supplying styling for shown snackbars. Mirrors
111
+ * Compose's `SnackbarHost(hostState) { data -> Snackbar(data, ...) }` lambda.
112
+ */
113
+ children?: React.ReactNode;
114
+ };
115
+
116
+ const SnackbarHostNativeView: React.ComponentType<SnackbarHostProps> = requireNativeView(
117
+ 'ExpoUI',
118
+ 'SnackbarHostView'
119
+ );
120
+
121
+ /**
122
+ * A Material 3 [SnackbarHost](https://developer.android.com/develop/ui/compose/components/snackbar)
123
+ * that displays snackbars triggered via its ref's `showSnackbar` method.
124
+ */
125
+ export function SnackbarHost(props: SnackbarHostProps) {
126
+ const { modifiers, children, ...rest } = props;
127
+ return (
128
+ <SnackbarHostNativeView
129
+ modifiers={modifiers}
130
+ {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
131
+ {...rest}>
132
+ {children}
133
+ </SnackbarHostNativeView>
134
+ );
135
+ }
@@ -1,8 +1,6 @@
1
1
  import { requireNativeView } from 'expo';
2
2
 
3
- import { type ObservableState } from '../../State/useNativeState';
4
- import { useWorkletProp } from '../../State/useWorkletProp';
5
- import { getStateId } from '../../State/utils';
3
+ import { getStateId, type ObservableState, useWorkletProp } from '../../State';
6
4
  import { type ModifierConfig } from '../../types';
7
5
  import { createViewModifierEventListener } from '../modifiers/utils';
8
6
 
@@ -2,10 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import type { Ref } from 'react';
3
3
  import type { ColorValue } from 'react-native';
4
4
 
5
- import { worklets } from '../../State/optionalWorklets';
6
- import type { ObservableState } from '../../State/useNativeState';
7
- import { useWorkletProp } from '../../State/useWorkletProp';
8
- import { getStateId } from '../../State/utils';
5
+ import { getStateId, type ObservableState, useWorkletProp, worklets } from '../../State';
9
6
  import type { ModifierConfig, ViewEvent } from '../../types';
10
7
  import { Slot } from '../SlotView';
11
8
  import { createViewModifierEventListener } from '../modifiers/utils';
@@ -1,4 +1,3 @@
1
- import '../State/index.fx';
2
1
  import './MaterialSymbolsAssetsTransformer.fx';
3
2
 
4
3
  export * from './AlertDialog';
@@ -49,6 +48,7 @@ export {
49
48
  type HorizontalPagerProps,
50
49
  } from './HorizontalPager';
51
50
  export * from './SearchBar';
51
+ export * from './Snackbar';
52
52
  export * from './DockedSearchBar';
53
53
  export * from './HorizontalFloatingToolbar';
54
54
  export * from './FloatingActionButton';
@@ -57,12 +57,13 @@ export * from './RadioButton';
57
57
  export * from './Surface';
58
58
  export { type TextProps, Text } from './Text';
59
59
  export * from './Tooltip';
60
+ export * from './LoadingIndicator';
60
61
 
61
62
  export * from './AnimatedVisibility';
62
63
  export * from './Box';
63
64
  export * from './Row';
64
65
  export * from './Column';
65
66
  export * from './FlowRow';
66
- export { useNativeState } from '../State/useNativeState';
67
+ export { useNativeState } from '../State';
67
68
  export type { ViewEvent } from '../types';
68
69
  export type { PrimitiveBaseProps } from './layout-types';
@@ -1,6 +1,6 @@
1
1
  import { type ColorValue } from 'react-native';
2
2
 
3
- import { type AnimatedValue } from './animation';
3
+ import { type AnimatedValue, type AnimationSpec } from './animation';
4
4
  import { createModifier, createModifierWithEventListener } from './createModifier';
5
5
  export { type ExpoModifier, type ModifierConfig } from '../../types';
6
6
  export {
@@ -142,9 +142,12 @@ export const offset = (x: number, y: number) => createModifier('offset', { x, y
142
142
 
143
143
  /**
144
144
  * Sets the background color.
145
+ * Pass an `animationSpec` to smoothly animate between colors when the prop changes (backed by `animateColorAsState`).
145
146
  * @param color - A color string (hex, e.g., `'#FF0000'`).
147
+ * @param options.animationSpec - Optional spec — animate between color changes.
146
148
  */
147
- export const background = (color: ColorValue) => createModifier('background', { color });
149
+ export const background = (color: ColorValue, options?: { animationSpec?: AnimationSpec }) =>
150
+ createModifier('background', { color, animationSpec: options?.animationSpec });
148
151
 
149
152
  /**
150
153
  * Adds a border around the view.
@@ -1,5 +1,5 @@
1
1
  import { requireNativeView } from 'expo';
2
- import type { ComponentType } from 'react';
2
+ import { useState, type ComponentType } from 'react';
3
3
  import type { NativeSyntheticEvent } from 'react-native';
4
4
 
5
5
  import { createViewModifierEventListener } from '../modifiers/utils';
@@ -21,6 +21,10 @@ export type BottomSheetProps = {
21
21
  * Callback function that is called when the `BottomSheet` presented state changes.
22
22
  */
23
23
  onIsPresentedChange: (isPresented: boolean) => void;
24
+ /**
25
+ * Callback function that is called after the `BottomSheet` has been fully dismissed.
26
+ */
27
+ onDismiss?: () => void;
24
28
  /**
25
29
  * When `true`, the sheet will automatically size itself to fit its content.
26
30
  * This sets the presentation detent to match the height of the children.
@@ -29,8 +33,9 @@ export type BottomSheetProps = {
29
33
  fitToContents?: boolean;
30
34
  } & CommonViewModifierProps;
31
35
 
32
- type NativeBottomSheetProps = Omit<BottomSheetProps, 'onIsPresentedChange'> & {
36
+ type NativeBottomSheetProps = Omit<BottomSheetProps, 'onIsPresentedChange' | 'onDismiss'> & {
33
37
  onIsPresentedChange: (event: NativeSyntheticEvent<{ isPresented: boolean }>) => void;
38
+ onDismiss: (event: NativeSyntheticEvent<object>) => void;
34
39
  };
35
40
 
36
41
  const BottomSheetNativeView: ComponentType<NativeBottomSheetProps> = requireNativeView(
@@ -38,23 +43,35 @@ const BottomSheetNativeView: ComponentType<NativeBottomSheetProps> = requireNati
38
43
  'BottomSheetView'
39
44
  );
40
45
 
41
- function transformBottomSheetProps(props: BottomSheetProps): NativeBottomSheetProps {
42
- const { modifiers, ...restProps } = props;
43
- return {
44
- modifiers,
45
- ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
46
- ...restProps,
47
- onIsPresentedChange: ({ nativeEvent: { isPresented } }) => {
48
- props?.onIsPresentedChange?.(isPresented);
49
- },
50
- };
51
- }
52
-
53
46
  /**
54
47
  * `BottomSheet` presents content from the bottom of the screen.
55
48
  */
56
49
  function BottomSheet(props: BottomSheetProps) {
57
- return <BottomSheetNativeView {...transformBottomSheetProps(props)} />;
50
+ const { modifiers, onIsPresentedChange, onDismiss, ...restProps } = props;
51
+ const [isMounted, setIsMounted] = useState(props.isPresented);
52
+
53
+ if (props.isPresented && !isMounted) {
54
+ setIsMounted(true);
55
+ }
56
+
57
+ if (!isMounted) {
58
+ return null;
59
+ }
60
+
61
+ return (
62
+ <BottomSheetNativeView
63
+ modifiers={modifiers}
64
+ {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
65
+ {...restProps}
66
+ onIsPresentedChange={({ nativeEvent: { isPresented } }) => {
67
+ onIsPresentedChange?.(isPresented);
68
+ }}
69
+ onDismiss={() => {
70
+ setIsMounted(false);
71
+ onDismiss?.();
72
+ }}
73
+ />
74
+ );
58
75
  }
59
76
 
60
77
  export { BottomSheet };
@@ -45,6 +45,7 @@ export type HostProps = {
45
45
 
46
46
  children: React.ReactNode;
47
47
  style?: StyleProp<ViewStyle>;
48
+ pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto';
48
49
  } & CommonViewModifierProps;
49
50
 
50
51
  const HostNativeView: React.ComponentType<
@@ -3,6 +3,32 @@ import { requireNativeView } from 'expo';
3
3
  import { createViewModifierEventListener } from '../modifiers/utils';
4
4
  import { type CommonViewModifierProps } from '../types';
5
5
 
6
+ /**
7
+ * Scroll phase emitted by the `onScrollPhaseChange(...)` modifier. Mirrors
8
+ * SwiftUI's `ScrollPhase` (iOS 18+).
9
+ */
10
+ export type ScrollPhase = 'idle' | 'tracking' | 'interacting' | 'animating' | 'decelerating';
11
+
12
+ /**
13
+ * Snapshot of a `ScrollView`'s scroll geometry, emitted by the
14
+ * `useScrollGeometryChange(...)` and `onScrollPhaseChange(...)` modifiers
15
+ * (iOS 18+).
16
+ */
17
+ export type ScrollGeometry = {
18
+ /** Horizontal content offset, in points. */
19
+ contentOffsetX: number;
20
+ /** Vertical content offset, in points. */
21
+ contentOffsetY: number;
22
+ /** Width of the visible scroll container, in points. */
23
+ containerWidth: number;
24
+ /** Height of the visible scroll container, in points. */
25
+ containerHeight: number;
26
+ /** Total width of the scrollable content, in points. */
27
+ contentWidth: number;
28
+ /** Total height of the scrollable content, in points. */
29
+ contentHeight: number;
30
+ };
31
+
6
32
  export type ScrollViewProps = {
7
33
  children: React.ReactNode;
8
34
  /**
@@ -23,8 +49,15 @@ const ScrollViewNativeView: React.ComponentType<ScrollViewProps> = requireNative
23
49
  'ScrollViewComponent'
24
50
  );
25
51
 
52
+ /**
53
+ * SwiftUI `ScrollView` wrapper. To control scroll position, pair this with the
54
+ * `scrollPosition(state, { onChange })` modifier and a `useNativeState`-backed
55
+ * id. Write `state.value = targetId` for an instant scroll, or wrap the write
56
+ * in `withAnimation(...)` from `@expo/ui/swift-ui` for an animated one.
57
+ */
26
58
  export function ScrollView(props: ScrollViewProps) {
27
59
  const { modifiers, ...restProps } = props;
60
+
28
61
  return (
29
62
  <ScrollViewNativeView
30
63
  modifiers={modifiers}
@@ -1,10 +1,7 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import type { Ref } from 'react';
3
3
 
4
- import { worklets } from '../../State/optionalWorklets';
5
- import type { ObservableState } from '../../State/useNativeState';
6
- import { useWorkletProp } from '../../State/useWorkletProp';
7
- import { getStateId } from '../../State/utils';
4
+ import { getStateId, type ObservableState, useWorkletProp, worklets } from '../../State';
8
5
  import type { ViewEvent } from '../../types';
9
6
  import { Slot } from '../SlotView';
10
7
  import { createViewModifierEventListener } from '../modifiers/utils';
@@ -1,9 +1,7 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import { type SFSymbol } from 'sf-symbols-typescript';
3
3
 
4
- import { type ObservableState } from '../../State/useNativeState';
5
- import { useWorkletProp } from '../../State/useWorkletProp';
6
- import { getStateId } from '../../State/utils';
4
+ import { getStateId, type ObservableState, useWorkletProp } from '../../State';
7
5
  import { createViewModifierEventListener } from '../modifiers/utils';
8
6
  import { type CommonViewModifierProps } from '../types';
9
7
 
@@ -1,10 +1,7 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import type { Ref } from 'react';
3
3
 
4
- import { worklets } from '../../State/optionalWorklets';
5
- import type { ObservableState } from '../../State/useNativeState';
6
- import { useWorkletProp } from '../../State/useWorkletProp';
7
- import { getStateId } from '../../State/utils';
4
+ import { getStateId, type ObservableState, useWorkletProp, worklets } from '../../State';
8
5
  import type { ViewEvent } from '../../types';
9
6
  import { Slot } from '../SlotView';
10
7
  import { createViewModifierEventListener } from '../modifiers/utils';
@@ -1,5 +1,3 @@
1
- import '../State/index.fx';
2
-
3
1
  export * from './AccessoryWidgetBackground';
4
2
  export * from './Alert';
5
3
  export * from './BottomSheet';
@@ -36,7 +34,8 @@ export * from './Spacer';
36
34
  export * from './Stepper';
37
35
  export * from './SwipeActions';
38
36
  export * from './Text';
39
- export { useNativeState } from '../State/useNativeState';
37
+ export { useNativeState } from '../State';
38
+ export { withAnimation, type WithAnimationCompletionCriteria } from './withAnimation';
40
39
  export * from './SyncToggle';
41
40
  export * from './TabView';
42
41
  export * from './Toggle';
@@ -19,6 +19,7 @@ import { datePickerStyle } from './datePickerStyle';
19
19
  import { environment } from './environment';
20
20
  import { gaugeStyle } from './gaugeStyle';
21
21
  import { progressViewStyle } from './progressViewStyle';
22
+ import { onScrollPhaseChange, useScrollGeometryChange } from './scrollObservation';
22
23
  import { id, scrollPosition } from './scrollPosition';
23
24
  import { symbolEffect } from './symbolEffect';
24
25
  import type { Color } from './types';
@@ -981,30 +982,33 @@ export const listSectionMargins = (params?: {
981
982
 
982
983
  /**
983
984
  * Sets the font properties of a view.
984
- * Supports both custom font families and system fonts with weight and design options.
985
985
  *
986
- * @param params - The font configuration. When `family` is provided, it uses Font.custom().
987
- * When `family` is not provided, it uses Font.system() with the specified weight and design.
986
+ * Pass `textStyle` to scale with the user's Dynamic Type setting. Combine
987
+ * it with `family` to scale a custom font.
988
988
  *
989
989
  * @example
990
990
  * ```tsx
991
- * // Custom font family
992
- * <Text modifiers={[font({ family: 'Helvetica', size: 18 })]}>Custom Font Text</Text>
991
+ * // Scales with Dynamic Type
992
+ * <Text modifiers={[font({ textStyle: 'largeTitle', weight: 'bold' })]}>Hello</Text>
993
993
  *
994
- * // System font with weight and design
995
- * <Text modifiers={[font({ weight: 'bold', design: 'rounded', size: 16 })]}>System Font Text</Text>
994
+ * // Custom font that scales relative to the body text style
995
+ * <Text modifiers={[font({ textStyle: 'body', family: 'Helvetica', size: 18 })]}>Hi</Text>
996
+ *
997
+ * // Fixed-size system font (no Dynamic Type scaling)
998
+ * <Text modifiers={[font({ weight: 'bold', design: 'rounded', size: 16 })]}>Static</Text>
996
999
  * ```
997
- * @see Official [SwiftUI documentation for `custom(_:size:)`](https://developer.apple.com/documentation/swiftui/font/custom(_:size:)) and Official [SwiftUI documentation for `system(size:weight:design:)`](https://developer.apple.com/documentation/swiftui/font/system(size:weight:design:)).
1000
+ * @see Official SwiftUI documentation for [`system(_:design:weight:)`](https://developer.apple.com/documentation/swiftui/font/system(_:design:weight:)), and [`custom(_:size:relativeTo:)`](https://developer.apple.com/documentation/swiftui/font/custom(_:size:relativeto:)).
998
1001
  */
999
1002
  export const font = (params: {
1003
+ /** Custom font family name. */
1004
+ family?: string;
1000
1005
  /**
1001
- * Custom font family name.
1002
- * If provided, uses `Font.custom()`.
1006
+ * Font size in points. Ignored when only `textStyle` is set.
1007
+ *
1008
+ * @default 17
1003
1009
  */
1004
- family?: string;
1005
- /** Font size in points. */
1006
1010
  size?: number;
1007
- /** Font weight for system fonts. */
1011
+ /** Font weight. */
1008
1012
  weight?:
1009
1013
  | 'ultraLight'
1010
1014
  | 'thin'
@@ -1015,8 +1019,24 @@ export const font = (params: {
1015
1019
  | 'bold'
1016
1020
  | 'heavy'
1017
1021
  | 'black';
1018
- /** Font design for system fonts */
1022
+ /** Font design. Applied when no `family` is provided. `Font.custom` always uses the embedded font's own design. */
1019
1023
  design?: 'default' | 'rounded' | 'serif' | 'monospaced';
1024
+ /**
1025
+ * SwiftUI text style. When set, the resulting font scales with the user's
1026
+ * Dynamic Type setting.
1027
+ */
1028
+ textStyle?:
1029
+ | 'largeTitle'
1030
+ | 'title'
1031
+ | 'title2'
1032
+ | 'title3'
1033
+ | 'headline'
1034
+ | 'subheadline'
1035
+ | 'body'
1036
+ | 'callout'
1037
+ | 'footnote'
1038
+ | 'caption'
1039
+ | 'caption2';
1020
1040
  }) => createModifier('font', params);
1021
1041
  /**
1022
1042
  * Asks grid layouts not to offer the view extra size in the specified axes.
@@ -1339,6 +1359,8 @@ export type BuiltInModifier =
1339
1359
  | ReturnType<typeof scrollTargetLayout>
1340
1360
  | ReturnType<typeof id>
1341
1361
  | ReturnType<typeof scrollPosition>
1362
+ | ReturnType<typeof onScrollPhaseChange>
1363
+ | NonNullable<ReturnType<typeof useScrollGeometryChange>>
1342
1364
  | ReturnType<typeof moveDisabled>
1343
1365
  | ReturnType<typeof deleteDisabled>
1344
1366
  | ReturnType<typeof environment>
@@ -1429,6 +1451,7 @@ export * from './presentationModifiers';
1429
1451
  export * from './environment';
1430
1452
  export * from './scrollPosition';
1431
1453
  export * from './symbolEffect';
1454
+ export * from './scrollObservation';
1432
1455
  export * from './widgets';
1433
1456
  export type {
1434
1457
  TimingAnimationParams,
@@ -0,0 +1,80 @@
1
+ import { getStateId, useWorkletProp, worklets } from '../../State';
2
+ import type { ScrollGeometry, ScrollPhase } from '../ScrollView';
3
+ import { createModifier, createModifierWithEventListener } from './createModifier';
4
+
5
+ /**
6
+ * Fires when the scroll geometry changes — i.e., on every scroll update and
7
+ * on container/content size changes. Use to drive continuous progress UI
8
+ * such as page indicators, parallax, or fractional offsets.
9
+ *
10
+ * If the callback is marked with the `'worklet'` directive, it runs
11
+ * synchronously on the UI thread (no JS-thread round-trip); otherwise it is
12
+ * delivered asynchronously as a regular JS event. Both paths share the same
13
+ * native modifier — the worklet variant is automatically wrapped in a
14
+ * `WorkletCallback` shared object whose lifetime is managed by the hook.
15
+ *
16
+ * This is a hook because the worklet path requires a stable shared-object
17
+ * reference across renders. Call it at the top of your component, then
18
+ * include the returned modifier in your `modifiers` array.
19
+ *
20
+ * Apply to a SwiftUI `ScrollView` (and other scrollable views). On iOS below
21
+ * 18.0 the modifier is a no-op.
22
+ *
23
+ * @platform ios 18.0+
24
+ * @platform tvos 18.0+
25
+ *
26
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/onscrollgeometrychange(for:of:_:)).
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const geometryModifier = useScrollGeometryChange((g) => {
31
+ * 'worklet';
32
+ * progress.value = g.contentOffsetX / g.containerWidth;
33
+ * });
34
+ *
35
+ * <ScrollView modifiers={[geometryModifier]} />
36
+ * ```
37
+ */
38
+ export function useScrollGeometryChange(callback?: (geometry: ScrollGeometry) => void) {
39
+ const isWorklet = !!callback && !!worklets?.isWorkletFunction?.(callback);
40
+ const workletCallback = useWorkletProp(
41
+ isWorklet ? callback : undefined,
42
+ 'onScrollGeometryChange'
43
+ );
44
+
45
+ if (!callback) {
46
+ return null;
47
+ }
48
+ if (isWorklet && workletCallback) {
49
+ return createModifier('onScrollGeometryChange', {
50
+ workletCallback: getStateId(workletCallback),
51
+ });
52
+ }
53
+ return createModifierWithEventListener('onScrollGeometryChange', (event: ScrollGeometry) =>
54
+ callback(event)
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Fires when SwiftUI's scroll phase changes (e.g., the user begins dragging,
60
+ * the scroll view starts decelerating, or scrolling settles to idle). The
61
+ * second argument is the scroll geometry sampled at the phase transition,
62
+ * useful for reading the final offset on settle without subscribing to
63
+ * per-frame `onScrollGeometryChange`.
64
+ *
65
+ * Apply to a SwiftUI `ScrollView` (and other scrollable views). On iOS below
66
+ * 18.0 the modifier is a no-op.
67
+ *
68
+ * @platform ios 18.0+
69
+ * @platform tvos 18.0+
70
+ *
71
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/onscrollphasechange(_:)).
72
+ */
73
+ export const onScrollPhaseChange = (
74
+ callback: (phase: ScrollPhase, geometry: ScrollGeometry) => void
75
+ ) =>
76
+ createModifierWithEventListener(
77
+ 'onScrollPhaseChange',
78
+ (event: { phase: ScrollPhase; geometry: ScrollGeometry }) =>
79
+ callback(event.phase, event.geometry)
80
+ );