@expo/ui 57.0.10 → 57.0.12

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 (96) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +21 -1
  4. package/android/src/main/java/expo/modules/ui/graphics/DrawablePainter.kt +43 -0
  5. package/android/src/main/java/expo/modules/ui/{icon/VectorIconLoader.kt → graphics/ImageLoader.kt} +122 -87
  6. package/android/src/main/java/expo/modules/ui/graphics/ImageSource.kt +28 -0
  7. package/android/src/main/java/expo/modules/ui/{icon → graphics}/ResourceIdHelper.kt +1 -1
  8. package/android/src/main/java/expo/modules/ui/icon/IconView.kt +7 -78
  9. package/android/src/main/java/expo/modules/ui/image/ImageView.kt +136 -0
  10. package/build/State/useWorkletProp.d.ts.map +1 -1
  11. package/build/jetpack-compose/Image/index.d.ts +107 -0
  12. package/build/jetpack-compose/Image/index.d.ts.map +1 -0
  13. package/build/jetpack-compose/index.d.ts +1 -0
  14. package/build/jetpack-compose/index.d.ts.map +1 -1
  15. package/build/swift-ui/Alert/index.d.ts.map +1 -1
  16. package/build/swift-ui/ConfirmationDialog/index.d.ts.map +1 -1
  17. package/build/swift-ui/Overlay/index.d.ts.map +1 -1
  18. package/build/swift-ui/Popover/index.d.ts.map +1 -1
  19. package/build/swift-ui/Slider/index.d.ts.map +1 -1
  20. package/build/swift-ui/modifiers/index.d.ts +61 -9
  21. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  22. package/build/universal/BottomSheet/index.android.d.ts +1 -1
  23. package/build/universal/BottomSheet/index.android.d.ts.map +1 -1
  24. package/build/universal/BottomSheet/index.d.ts +1 -1
  25. package/build/universal/BottomSheet/index.d.ts.map +1 -1
  26. package/build/universal/BottomSheet/index.ios.d.ts +1 -1
  27. package/build/universal/BottomSheet/index.ios.d.ts.map +1 -1
  28. package/build/universal/BottomSheet/types.d.ts +28 -0
  29. package/build/universal/BottomSheet/types.d.ts.map +1 -1
  30. package/build/universal/BottomSheet/utils.d.ts +13 -0
  31. package/build/universal/BottomSheet/utils.d.ts.map +1 -0
  32. package/expo-module.config.json +1 -1
  33. package/ios/Convertibles/PaddingValue.swift +57 -0
  34. package/ios/Modifiers/MenuIndicatorModifier.swift +18 -0
  35. package/ios/Modifiers/MenuStyleModifier.swift +28 -0
  36. package/ios/Modifiers/ViewModifierRegistry.swift +105 -18
  37. package/ios/SliderView.swift +11 -5
  38. package/ios/State/WorkletCallbackClassDef.swift +7 -0
  39. package/ios/Toggle/ToggleView.swift +16 -18
  40. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.10/expo.modules.ui-57.0.10-sources.jar → 57.0.12/expo.modules.ui-57.0.12-sources.jar} +0 -0
  41. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12-sources.jar.md5 +1 -0
  42. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12-sources.jar.sha1 +1 -0
  43. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12-sources.jar.sha256 +1 -0
  44. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12-sources.jar.sha512 +1 -0
  45. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.aar +0 -0
  46. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.aar.md5 +1 -0
  47. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.aar.sha1 +1 -0
  48. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.aar.sha256 +1 -0
  49. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.aar.sha512 +1 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.10/expo.modules.ui-57.0.10.module → 57.0.12/expo.modules.ui-57.0.12.module} +22 -22
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.module.md5 +1 -0
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.module.sha1 +1 -0
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.module.sha256 +1 -0
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.module.sha512 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.10/expo.modules.ui-57.0.10.pom → 57.0.12/expo.modules.ui-57.0.12.pom} +1 -1
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.pom.md5 +1 -0
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.pom.sha1 +1 -0
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.pom.sha256 +1 -0
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.12/expo.modules.ui-57.0.12.pom.sha512 +1 -0
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  62. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  63. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  64. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  65. package/package.json +4 -4
  66. package/src/State/useWorkletProp.ts +31 -14
  67. package/src/jetpack-compose/Image/index.tsx +149 -0
  68. package/src/jetpack-compose/index.ts +1 -0
  69. package/src/swift-ui/Alert/index.tsx +1 -0
  70. package/src/swift-ui/ConfirmationDialog/index.tsx +1 -0
  71. package/src/swift-ui/Overlay/index.tsx +1 -0
  72. package/src/swift-ui/Popover/index.tsx +1 -0
  73. package/src/swift-ui/Slider/index.tsx +14 -8
  74. package/src/swift-ui/modifiers/index.ts +70 -8
  75. package/src/universal/BottomSheet/index.android.tsx +13 -2
  76. package/src/universal/BottomSheet/index.ios.tsx +9 -1
  77. package/src/universal/BottomSheet/index.tsx +17 -2
  78. package/src/universal/BottomSheet/types.ts +33 -0
  79. package/src/universal/BottomSheet/utils.ts +35 -0
  80. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10-sources.jar.md5 +0 -1
  81. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10-sources.jar.sha1 +0 -1
  82. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10-sources.jar.sha256 +0 -1
  83. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10-sources.jar.sha512 +0 -1
  84. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.aar +0 -0
  85. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.aar.md5 +0 -1
  86. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.aar.sha1 +0 -1
  87. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.aar.sha256 +0 -1
  88. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.aar.sha512 +0 -1
  89. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.module.md5 +0 -1
  90. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.module.sha1 +0 -1
  91. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.module.sha256 +0 -1
  92. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.module.sha512 +0 -1
  93. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.pom.md5 +0 -1
  94. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.pom.sha1 +0 -1
  95. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.pom.sha256 +0 -1
  96. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.10/expo.modules.ui-57.0.10.pom.sha512 +0 -1
@@ -0,0 +1 @@
1
+ 8feb6c5213343355b543cddfedde01251c5a8a9067aa01efb89fe663b2a0e04f3284ab62ed2c9a2f8e65305691f61dd248ba2fdbc3dc7415c5d09ced6425edd1
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "expo.modules.ui",
5
5
  "module": "expo.modules.ui",
6
- "version": "57.0.10",
6
+ "version": "57.0.12",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.ui-57.0.10.aar",
28
- "url": "expo.modules.ui-57.0.10.aar",
29
- "size": 1947071,
30
- "sha512": "65dd5f448fa7a5db1112becf801262b96f1ded388023a72616e1623ed9eb3fa1aaa25e479db58ee56aed07e97a1e5d72c3b66c4eecd6c8de39f67f92c1dbf993",
31
- "sha256": "497fae34b972934a9eb0ae4b1343527eb1e5f872ad97c44de758d4e3095cf2d9",
32
- "sha1": "128612487df27ee99651882fde63af29121cec37",
33
- "md5": "b64c5776b5d32cb3e45c463040737d7a"
27
+ "name": "expo.modules.ui-57.0.12.aar",
28
+ "url": "expo.modules.ui-57.0.12.aar",
29
+ "size": 1988885,
30
+ "sha512": "8feb6c5213343355b543cddfedde01251c5a8a9067aa01efb89fe663b2a0e04f3284ab62ed2c9a2f8e65305691f61dd248ba2fdbc3dc7415c5d09ced6425edd1",
31
+ "sha256": "cdc531cad279eabcfce2c71e778614be0e16c45c77222fe4fa51fcbd7991e30b",
32
+ "sha1": "2ba19351886430ec818130eab431ce8143a56693",
33
+ "md5": "4508cfcae86a661e2c66bbf15b5909b0"
34
34
  }
35
35
  ]
36
36
  },
@@ -141,13 +141,13 @@
141
141
  ],
142
142
  "files": [
143
143
  {
144
- "name": "expo.modules.ui-57.0.10.aar",
145
- "url": "expo.modules.ui-57.0.10.aar",
146
- "size": 1947071,
147
- "sha512": "65dd5f448fa7a5db1112becf801262b96f1ded388023a72616e1623ed9eb3fa1aaa25e479db58ee56aed07e97a1e5d72c3b66c4eecd6c8de39f67f92c1dbf993",
148
- "sha256": "497fae34b972934a9eb0ae4b1343527eb1e5f872ad97c44de758d4e3095cf2d9",
149
- "sha1": "128612487df27ee99651882fde63af29121cec37",
150
- "md5": "b64c5776b5d32cb3e45c463040737d7a"
144
+ "name": "expo.modules.ui-57.0.12.aar",
145
+ "url": "expo.modules.ui-57.0.12.aar",
146
+ "size": 1988885,
147
+ "sha512": "8feb6c5213343355b543cddfedde01251c5a8a9067aa01efb89fe663b2a0e04f3284ab62ed2c9a2f8e65305691f61dd248ba2fdbc3dc7415c5d09ced6425edd1",
148
+ "sha256": "cdc531cad279eabcfce2c71e778614be0e16c45c77222fe4fa51fcbd7991e30b",
149
+ "sha1": "2ba19351886430ec818130eab431ce8143a56693",
150
+ "md5": "4508cfcae86a661e2c66bbf15b5909b0"
151
151
  }
152
152
  ]
153
153
  },
@@ -161,13 +161,13 @@
161
161
  },
162
162
  "files": [
163
163
  {
164
- "name": "expo.modules.ui-57.0.10-sources.jar",
165
- "url": "expo.modules.ui-57.0.10-sources.jar",
166
- "size": 95127,
167
- "sha512": "6773d96e9e4bc26584cb012c44b9e9f6afd9d4d099aa7c93eb35a2089a77b71155a671302db4a9f9d97267c310ad266476d2bbb04ce77cc4fa36f7b3e262e696",
168
- "sha256": "daa8952f564fc49bf1e8248beaa2f2bc5f305c02f01f9ea9f9209323034d7cdf",
169
- "sha1": "ab9529856e2d9014c4500bd026ef87271d46390e",
170
- "md5": "861e2a9fc90c0f77fb3e0c4b1e733bda"
164
+ "name": "expo.modules.ui-57.0.12-sources.jar",
165
+ "url": "expo.modules.ui-57.0.12-sources.jar",
166
+ "size": 98169,
167
+ "sha512": "bee3386af6f699916c5f3b09c72510621403743e5e887bf0e60d13142304a97964289f39f1fe79355ca9127cc02dfc88cf2d21bbfa4638ed4b9446de05030856",
168
+ "sha256": "3f5266c79c694c7d3ae5a885827d2dfb4ae922a9f66fceca406cd7556b753ac0",
169
+ "sha1": "dabe60c8f675eb6123187ef607915dccbeb79789",
170
+ "md5": "46f030ed2d5faa9fc07df319df125512"
171
171
  }
172
172
  ]
173
173
  }
@@ -0,0 +1 @@
1
+ 64563430ecb895ad72e936b019402ccd7c9e6c89
@@ -0,0 +1 @@
1
+ a615e1f0a09d6a7e76aba8b907fde6ffae9af29c50e411d880ba36a30908ccdb
@@ -0,0 +1 @@
1
+ fab8c1e40a8d6e25b66e77c977f415a05a3c4f6b5dcafb461c3a1be165b8db57f21b6cce4b46446a7855090b367fd38ff678b6d48e807dc22712d5e207cd9336
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>expo.modules.ui</groupId>
11
11
  <artifactId>expo.modules.ui</artifactId>
12
- <version>57.0.10</version>
12
+ <version>57.0.12</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.ui</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ dcf4640e9a602afa20af004bae13cdcb182d7cc6
@@ -0,0 +1 @@
1
+ 3f88190776e8ff18c8b42b46ee0b8b4e2a827cf671160966b6a3e1e62301f913
@@ -0,0 +1 @@
1
+ 62c5c2b27746a876d4db64eef13f596835a2f876c0808457d1a82b38fc93e7e5e667a11a5cbcb603efa58ff10b9001981bb347f99b6bd009e452445c80299689
@@ -3,11 +3,11 @@
3
3
  <groupId>expo.modules.ui</groupId>
4
4
  <artifactId>expo.modules.ui</artifactId>
5
5
  <versioning>
6
- <latest>57.0.10</latest>
7
- <release>57.0.10</release>
6
+ <latest>57.0.12</latest>
7
+ <release>57.0.12</release>
8
8
  <versions>
9
- <version>57.0.10</version>
9
+ <version>57.0.12</version>
10
10
  </versions>
11
- <lastUpdated>20260810153637</lastUpdated>
11
+ <lastUpdated>20260820103851</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- da44b67308586f65f45fa25667245d1c
1
+ 990a1e6f470f0ae61d641c01c2510f97
@@ -1 +1 @@
1
- 22a8e047065b96bd73e0f7bb1405c504b5a2a8e1
1
+ a6d138ba3ecd66801bb35f9d23a69cef22d0b0e8
@@ -1 +1 @@
1
- a1e7bc770193ea69ab038636d960b7f19355c8d8edd8ce3c695b37415f38b7c5
1
+ d98cea313be8b245d8c8b9dd4455ddaa2f5259a8e9a55b19079afd698133e01e
@@ -1 +1 @@
1
- 40cf745d08c291af78fd00c69f643740133f5924549f799854510578ca89c37e026a8798477e3d5340f9a6f2674536364930445954526b8629fa14351ff1b906
1
+ 6d33dd52cf942431a652c01906b58f2765c791f457597f8757a98d8d6dfa2a2af5ed9715aca0c28ceb9ab615b01198d6126dd48f1745762560d41d845507f374
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/ui",
3
- "version": "57.0.10",
3
+ "version": "57.0.12",
4
4
  "description": "A collection of UI components",
5
5
  "sideEffects": [
6
6
  "*.fx.js",
@@ -96,8 +96,8 @@
96
96
  "@types/react": "~19.2.0",
97
97
  "react-native-reanimated": "4.5.1",
98
98
  "react-native-worklets": "0.10.1",
99
- "expo-module-scripts": "56.0.3",
100
- "expo": "57.0.12"
99
+ "expo": "57.0.15",
100
+ "expo-module-scripts": "56.0.3"
101
101
  },
102
102
  "jest": {
103
103
  "preset": "expo-module-scripts"
@@ -121,7 +121,7 @@
121
121
  "optional": true
122
122
  }
123
123
  },
124
- "gitHead": "9a08ef16f8abc1788dec0014612be5eeb5dc03af",
124
+ "gitHead": "499024a441d67293aa60afcbe02051999a54f1bb",
125
125
  "scripts": {
126
126
  "build": "expo-module build",
127
127
  "clean": "expo-module clean",
@@ -1,10 +1,23 @@
1
1
  import { requireNativeModule } from 'expo';
2
- import { type SharedObject, useReleasingSharedObject } from 'expo-modules-core';
2
+ import { type SharedObject, useReleasingSharedObjectWithLifecycle } from 'expo-modules-core';
3
3
 
4
4
  import { worklets } from './optionalWorklets';
5
5
 
6
6
  const ExpoUI = requireNativeModule('ExpoUI');
7
7
 
8
+ function createSerializable(callback?: (...args: any[]) => void, propName?: string) {
9
+ if (!callback || !worklets) {
10
+ return null;
11
+ }
12
+ if (!worklets.isWorkletFunction(callback)) {
13
+ const name = propName ? `'${propName}'` : 'The callback';
14
+ throw new Error(
15
+ `${name} must be a worklet function. Add the 'worklet' directive as the first statement in your callback.`
16
+ );
17
+ }
18
+ return worklets.createSerializable(callback);
19
+ }
20
+
8
21
  /**
9
22
  * Creates a `WorkletCallback` SharedObject that wraps a worklet function.
10
23
  * The SharedObject's integer ID survives React's prop serialization,
@@ -16,17 +29,21 @@ export function useWorkletProp(
16
29
  callback?: (...args: any[]) => void,
17
30
  propName?: string
18
31
  ): SharedObject | null {
19
- return useReleasingSharedObject(() => {
20
- if (!callback || !worklets) {
21
- return null;
22
- }
23
- try {
24
- return new ExpoUI.WorkletCallback(worklets.createSerializable(callback));
25
- } catch {
26
- const name = propName ? `'${propName}'` : 'The callback';
27
- throw new Error(
28
- `${name} must be a worklet function. Add the 'worklet' directive as the first statement in your callback.`
29
- );
30
- }
31
- }, [callback]);
32
+ return useReleasingSharedObjectWithLifecycle(
33
+ {
34
+ factory: () => {
35
+ const serializable = createSerializable(callback, propName);
36
+ return serializable ? new ExpoUI.WorkletCallback(serializable) : null;
37
+ },
38
+ // Only removing the callback needs a different object.
39
+ shouldRecreate: () => !callback,
40
+ update: (object) => {
41
+ const serializable = createSerializable(callback, propName);
42
+ if (serializable) {
43
+ object.setWorklet(serializable);
44
+ }
45
+ },
46
+ },
47
+ [callback]
48
+ );
32
49
  }
@@ -0,0 +1,149 @@
1
+ import { requireNativeView } from 'expo';
2
+ import {
3
+ Image as ReactNativeImage,
4
+ type ColorValue,
5
+ type ImageResolvedAssetSource,
6
+ } from 'react-native';
7
+
8
+ import { type ViewEvent } from '../../types';
9
+ import { type ContentAlignment, type PrimitiveBaseProps, transformProps } from '../layout-types';
10
+
11
+ /** Controls how an image is scaled inside its bounds. */
12
+ export type ImageContentScale =
13
+ | 'fit'
14
+ | 'crop'
15
+ | 'fillBounds'
16
+ | 'fillWidth'
17
+ | 'fillHeight'
18
+ | 'inside'
19
+ | 'none';
20
+
21
+ /** A bundled image or a single URI-based image source. */
22
+ export type ImageSource =
23
+ | number
24
+ | {
25
+ uri: string;
26
+ width?: number;
27
+ height?: number;
28
+ scale?: number;
29
+ };
30
+
31
+ export interface ImageProps extends PrimitiveBaseProps {
32
+ /**
33
+ * A bundled image or an object containing a single local or remote URI.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * <Image source={require('./photo.png')} />
38
+ * <Image source={{ uri: 'file:///path/to/photo.png' }} />
39
+ * ```
40
+ */
41
+ source: ImageSource;
42
+
43
+ /**
44
+ * How the image should be scaled to fit its bounds.
45
+ * @default 'fit'
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * <Image source={require('./photo.png')} contentScale="crop" />
50
+ * ```
51
+ */
52
+ contentScale?: ImageContentScale;
53
+
54
+ /**
55
+ * How the image should be aligned within its bounds.
56
+ * @default 'center'
57
+ */
58
+ alignment?: ContentAlignment;
59
+
60
+ /**
61
+ * Accessibility label for the image.
62
+ * Used by screen readers to describe the image to users.
63
+ * Use `null` for decorative images.
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * <Image
68
+ * source={require('./photo.png')}
69
+ * contentDescription="A mountain at sunset"
70
+ * />
71
+ * ```
72
+ */
73
+ contentDescription?: string | null;
74
+
75
+ /**
76
+ * Optional color applied to every non-transparent image pixel.
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * <Image source={require('./icon.png')} tint="#007AFF" />
81
+ * <Image source={require('./icon.png')} tint="blue" />
82
+ * ```
83
+ */
84
+ tint?: ColorValue;
85
+
86
+ /**
87
+ * Opacity of the image between `0` and `1`.
88
+ * @default 1
89
+ *
90
+ * @example
91
+ * ```tsx
92
+ * <Image source={require('./icon.png')} alpha={0.5} />
93
+ * ```
94
+ */
95
+ alpha?: number;
96
+
97
+ /**
98
+ * Callback that is called when the image loads successfully.
99
+ */
100
+ onLoad?: () => void;
101
+
102
+ /**
103
+ * Callback that is called when the image fails to load.
104
+ */
105
+ onError?: (error: string) => void;
106
+ }
107
+
108
+ /** @hidden */
109
+ export type NativeImageProps = Omit<ImageProps, 'source' | 'onLoad' | 'onError'> &
110
+ ViewEvent<'onLoad', void> &
111
+ ViewEvent<'onError', { error: string }> & {
112
+ source: ImageResolvedAssetSource | null;
113
+ };
114
+
115
+ const ImageNativeView: React.ComponentType<NativeImageProps> = requireNativeView(
116
+ 'ExpoUI',
117
+ 'ImageView'
118
+ );
119
+
120
+ /**
121
+ * Displays an image using Jetpack Compose.
122
+ *
123
+ * Size the image with modifiers such as `size`, `width`, `height`, or `fillMaxSize`.
124
+ *
125
+ * @example
126
+ * ```tsx
127
+ * import { Image } from '@expo/ui/jetpack-compose';
128
+ * import { size } from '@expo/ui/jetpack-compose/modifiers';
129
+ *
130
+ * <Image
131
+ * source={require('./photo.png')}
132
+ * contentScale="crop"
133
+ * contentDescription="A mountain at sunset"
134
+ * modifiers={[size(160, 100)]}
135
+ * />
136
+ * ```
137
+ */
138
+ export function Image(props: ImageProps) {
139
+ const { source, onLoad, onError, ...restProps } = props;
140
+
141
+ return (
142
+ <ImageNativeView
143
+ {...transformProps(restProps)}
144
+ source={ReactNativeImage.resolveAssetSource(source)}
145
+ onLoad={onLoad}
146
+ onError={onError ? ({ nativeEvent }) => onError(nativeEvent.error) : undefined}
147
+ />
148
+ );
149
+ }
@@ -10,6 +10,7 @@ export * from './Chip';
10
10
  export * from './Button';
11
11
  export * from './colors';
12
12
  export * from './Icon';
13
+ export * from './Image';
13
14
  export * from './IconButton';
14
15
  export * from './DropdownMenu';
15
16
  export * from './ExposedDropdownMenuBox';
@@ -73,6 +73,7 @@ function Alert(props: AlertProps) {
73
73
  return (
74
74
  <AlertNativeView
75
75
  {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
76
+ modifiers={modifiers}
76
77
  {...restProps}
77
78
  onIsPresentedChange={handleIsPresentedChange}>
78
79
  {children}
@@ -76,6 +76,7 @@ function ConfirmationDialog(props: ConfirmationDialogProps) {
76
76
  return (
77
77
  <ConfirmationDialogNativeView
78
78
  {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
79
+ modifiers={modifiers}
79
80
  {...restProps}
80
81
  onIsPresentedChange={handleIsPresentedChange}>
81
82
  {children}
@@ -30,6 +30,7 @@ export function Overlay(props: OverlayProps) {
30
30
  return (
31
31
  <OverlayNativeView
32
32
  {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
33
+ modifiers={modifiers}
33
34
  {...restProps}>
34
35
  {children}
35
36
  </OverlayNativeView>
@@ -56,6 +56,7 @@ export function Popover(props: PopoverViewProps) {
56
56
  return (
57
57
  <PopoverNativeView
58
58
  {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
59
+ modifiers={modifiers}
59
60
  {...restProps}
60
61
  isPresented={isPresented}
61
62
  onIsPresentedChange={handleIsPresentedChange}>
@@ -1,4 +1,5 @@
1
1
  import { requireNativeView } from 'expo';
2
+ import { useRef } from 'react';
2
3
  import type { NativeSyntheticEvent } from 'react-native';
3
4
 
4
5
  import { Slot } from '../SlotView';
@@ -57,7 +58,8 @@ type NativeSliderProps = Omit<
57
58
  SliderProps,
58
59
  'onValueChange' | 'onEditingChanged' | 'label' | 'minimumValueLabel' | 'maximumValueLabel'
59
60
  > & {
60
- onValueChanged?: (event: NativeSyntheticEvent<{ value: number }>) => void;
61
+ mostRecentEventCount?: number;
62
+ onValueChanged?: (event: NativeSyntheticEvent<{ value: number; eventCount: number }>) => void;
61
63
  onEditingChanged?: (event: NativeSyntheticEvent<{ isEditing: boolean }>) => void;
62
64
  children?: React.ReactNode;
63
65
  };
@@ -67,7 +69,10 @@ const SliderNativeView: React.ComponentType<NativeSliderProps> = requireNativeVi
67
69
  'SliderView'
68
70
  );
69
71
 
70
- function transformSliderProps(props: SliderProps): NativeSliderProps {
72
+ function transformSliderProps(
73
+ props: SliderProps,
74
+ eventCount: { current: number }
75
+ ): NativeSliderProps {
71
76
  const {
72
77
  label,
73
78
  minimumValueLabel,
@@ -78,11 +83,11 @@ function transformSliderProps(props: SliderProps): NativeSliderProps {
78
83
  } = props;
79
84
  return {
80
85
  ...restProps,
81
- onValueChanged: onValueChange
82
- ? ({ nativeEvent: { value } }) => {
83
- onValueChange(value);
84
- }
85
- : undefined,
86
+ mostRecentEventCount: eventCount.current,
87
+ onValueChanged: ({ nativeEvent: { value, eventCount: nativeEventCount } }) => {
88
+ eventCount.current = nativeEventCount;
89
+ onValueChange?.(value);
90
+ },
86
91
  onEditingChanged: onEditingChanged
87
92
  ? ({ nativeEvent: { isEditing } }) => {
88
93
  onEditingChanged(isEditing);
@@ -93,9 +98,10 @@ function transformSliderProps(props: SliderProps): NativeSliderProps {
93
98
 
94
99
  export function Slider(props: SliderProps) {
95
100
  const { label, minimumValueLabel, maximumValueLabel } = props;
101
+ const eventCount = useRef(0);
96
102
 
97
103
  return (
98
- <SliderNativeView {...transformSliderProps(props)}>
104
+ <SliderNativeView {...transformSliderProps(props, eventCount)}>
99
105
  {label && <Slot name="label">{label}</Slot>}
100
106
  {minimumValueLabel && <Slot name="minimum">{minimumValueLabel}</Slot>}
101
107
  {maximumValueLabel && <Slot name="maximum">{maximumValueLabel}</Slot>}
@@ -88,6 +88,23 @@ export const matchedGeometryEffect = (
88
88
  }
89
89
  ) => createModifier('matchedGeometryEffect', { id, namespaceId, ...options });
90
90
 
91
+ /**
92
+ * Isolates the geometry (e.g. position and size) of the view from its parent view.
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * <VStack modifiers={[animation(Animation.spring(), isBusy)]}>
97
+ * {isBusy ? <Text>Working…</Text> : null}
98
+ * <Button label="Check now" modifiers={[geometryGroup()]} />
99
+ * </VStack>
100
+ * ```
101
+ *
102
+ * @platform ios 17.0+
103
+ * @platform tvos 17.0+
104
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/geometrygroup()).
105
+ */
106
+ export const geometryGroup = () => createModifier('geometryGroup', {});
107
+
91
108
  /**
92
109
  * Sets the frame properties of a view.
93
110
  * @param params - The frame parameters. Width, height, minWidth, maxWidth, minHeight, maxHeight, idealWidth, idealHeight and alignment.
@@ -140,18 +157,26 @@ export const containerRelativeFrame = (params: {
140
157
 
141
158
  /**
142
159
  * Sets padding on a view.
143
- * Supports individual edges or shorthand properties.
160
+ * Supports individual edges or shorthand properties. Every edge accepts a length in points or
161
+ * `'default'` to apply the system default padding to that edge. Edges set by a specific property
162
+ * take precedence over the shorthand ones, and unspecified edges get no padding.
163
+ * Calling it without parameters applies the system default padding to all edges.
144
164
  * @param params - The padding parameters: `top`, `bottom`, `leading`, `trailing`, `horizontal`, `vertical` and `all`.
145
165
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/SwiftUI/View/padding(_:_:)).
166
+ * @example
167
+ * ```tsx
168
+ * // The system default padding on top, 12 points on the sides, nothing at the bottom.
169
+ * <Text modifiers={[padding({ top: 'default', horizontal: 12 })]}>Hello</Text>
170
+ * ```
146
171
  */
147
172
  export const padding = (params?: {
148
- top?: number;
149
- bottom?: number;
150
- leading?: number;
151
- trailing?: number;
152
- horizontal?: number;
153
- vertical?: number;
154
- all?: number;
173
+ top?: number | 'default';
174
+ bottom?: number | 'default';
175
+ leading?: number | 'default';
176
+ trailing?: number | 'default';
177
+ horizontal?: number | 'default';
178
+ vertical?: number | 'default';
179
+ all?: number | 'default';
155
180
  }) => createModifier('padding', params);
156
181
 
157
182
  /**
@@ -671,6 +696,30 @@ export const buttonBorderShape = (
671
696
  export const toggleStyle = (style: 'automatic' | 'switch' | 'button') =>
672
697
  createModifier('toggleStyle', { style });
673
698
 
699
+ /**
700
+ * Sets the style for menus within this view.
701
+ * @param style - The menu style. Combine `'button'` with `buttonStyle('plain')` and
702
+ * `menuIndicator('hidden')` to render the menu's own label as the entire trigger, without any
703
+ * surrounding button chrome or disclosure chevron.
704
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/menustyle(_:)).
705
+ * @platform ios
706
+ * @platform tvos 17.0+
707
+ */
708
+ export const menuStyle = (style: 'automatic' | 'button') => createModifier('menuStyle', { style });
709
+
710
+ /**
711
+ * Sets the visibility of the menu indicator, the disclosure chevron drawn next to a menu's label.
712
+ * @param visibility - Indicator visibility:
713
+ * - `'automatic'`: platform-default behavior.
714
+ * - `'visible'`: show the indicator.
715
+ * - `'hidden'`: hide the indicator.
716
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/menuindicator(_:)).
717
+ * @platform ios
718
+ * @platform tvos 17.0+
719
+ */
720
+ export const menuIndicator = (visibility: 'automatic' | 'visible' | 'hidden') =>
721
+ createModifier('menuIndicator', { visibility });
722
+
674
723
  /**
675
724
  * Sets the size of controls within this view.
676
725
  * @param size - The control size.
@@ -1063,6 +1112,15 @@ export const listRowSeparator = (
1063
1112
  edges?: 'all' | 'top' | 'bottom'
1064
1113
  ) => createModifier('listRowSeparator', { visibility, edges });
1065
1114
 
1115
+ /**
1116
+ * Sets the tint color of the separator for a list row.
1117
+ * @param color - The color to apply to the separator (hex string). For example, `#FF0000`. When omitted, the default separator color is used.
1118
+ * @param edges - The edges where the separator tint applies.
1119
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/listrowseparatortint(_:edges:)).
1120
+ */
1121
+ export const listRowSeparatorTint = (color?: Color, edges?: 'all' | 'top' | 'bottom') =>
1122
+ createModifier('listRowSeparatorTint', { color, edges });
1123
+
1066
1124
  /**
1067
1125
  * Sets the vertical spacing between adjacent rows in a list.
1068
1126
  * @param spacing - The spacing value to use. When omitted, the default spacing is used.
@@ -1637,6 +1695,8 @@ export type BuiltInModifier =
1637
1695
  | ReturnType<typeof buttonStyle>
1638
1696
  | ReturnType<typeof buttonBorderShape>
1639
1697
  | ReturnType<typeof toggleStyle>
1698
+ | ReturnType<typeof menuStyle>
1699
+ | ReturnType<typeof menuIndicator>
1640
1700
  | ReturnType<typeof controlSize>
1641
1701
  | ReturnType<typeof imageScale>
1642
1702
  | ReturnType<typeof labelStyle>
@@ -1660,6 +1720,7 @@ export type BuiltInModifier =
1660
1720
  | ReturnType<typeof clipped>
1661
1721
  | ReturnType<typeof glassEffect>
1662
1722
  | ReturnType<typeof glassEffectId>
1723
+ | ReturnType<typeof geometryGroup>
1663
1724
  | ReturnType<typeof animation>
1664
1725
  | ReturnType<typeof containerShape>
1665
1726
  | ReturnType<typeof contentShape>
@@ -1680,6 +1741,7 @@ export type BuiltInModifier =
1680
1741
  | ReturnType<typeof environment>
1681
1742
  | ReturnType<typeof listRowBackground>
1682
1743
  | ReturnType<typeof listRowSeparator>
1744
+ | ReturnType<typeof listRowSeparatorTint>
1683
1745
  | ReturnType<typeof listRowSpacing>
1684
1746
  | ReturnType<typeof truncationMode>
1685
1747
  | ReturnType<typeof allowsTightening>
@@ -8,6 +8,7 @@ import {
8
8
  import { useEffect, useRef, useState } from 'react';
9
9
 
10
10
  import type { BottomSheetProps, SnapPoint } from './types';
11
+ import { resolveContentPadding } from './utils';
11
12
 
12
13
  // M3 `ModalBottomSheet` only has partial/expanded states.
13
14
  // Only allow the partial state when the consumer requested a partial-friendly snap point.
@@ -38,6 +39,8 @@ export function BottomSheet({
38
39
  snapPoints,
39
40
  testID,
40
41
  modifiers,
42
+ shouldDismissOnBackPress = true,
43
+ contentPadding,
41
44
  }: BottomSheetProps) {
42
45
  const sheetRef = useRef<ModalBottomSheetRef>(null);
43
46
  const [mount, setMount] = useState(isPresented);
@@ -60,7 +63,15 @@ export function BottomSheet({
60
63
  return null;
61
64
  }
62
65
 
63
- const contentModifiers: ModifierConfig[] = [padding(16, showDragIndicator ? 0 : 16, 16, 0)];
66
+ // When the drag handle is hidden, the default inset adds top padding so content doesn't crop
67
+ // against the top edge of the sheet.
68
+ const { top, bottom, left, right } = resolveContentPadding(contentPadding, {
69
+ top: showDragIndicator ? 0 : 16,
70
+ bottom: 0,
71
+ left: 16,
72
+ right: 16,
73
+ });
74
+ const contentModifiers: ModifierConfig[] = [padding(left, top, right, bottom)];
64
75
  if (shouldFillMaxHeight(snapPoints)) contentModifiers.push(fillMaxHeight());
65
76
  if (testID) contentModifiers.push(testIDModifier(testID));
66
77
 
@@ -71,8 +82,8 @@ export function BottomSheet({
71
82
  onDismissRequest={onDismiss}
72
83
  showDragHandle={showDragIndicator}
73
84
  skipPartiallyExpanded={shouldSkipPartiallyExpanded(snapPoints)}
85
+ properties={{ shouldDismissOnBackPress }}
74
86
  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
87
  <Column modifiers={contentModifiers}>{children}</Column>
77
88
  </ModalBottomSheet>
78
89
  </Host>