@expo/ui 57.0.11 → 57.0.13

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 (93) 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 +14 -1
  4. package/android/src/main/java/expo/modules/ui/ModalBottomSheetView.kt +50 -0
  5. package/android/src/main/java/expo/modules/ui/graphics/DrawablePainter.kt +43 -0
  6. package/android/src/main/java/expo/modules/ui/{icon/VectorIconLoader.kt → graphics/ImageLoader.kt} +122 -87
  7. package/android/src/main/java/expo/modules/ui/graphics/ImageSource.kt +28 -0
  8. package/android/src/main/java/expo/modules/ui/{icon → graphics}/ResourceIdHelper.kt +1 -1
  9. package/android/src/main/java/expo/modules/ui/icon/IconView.kt +7 -78
  10. package/android/src/main/java/expo/modules/ui/image/ImageView.kt +136 -0
  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 +37 -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 +42 -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/ViewModifierRegistry.swift +110 -18
  35. package/ios/RNHostView.swift +5 -0
  36. package/ios/SliderView.swift +11 -5
  37. package/ios/Toggle/ToggleView.swift +16 -18
  38. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.11/expo.modules.ui-57.0.11-sources.jar → 57.0.13/expo.modules.ui-57.0.13-sources.jar} +0 -0
  39. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.md5 +1 -0
  40. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.sha1 +1 -0
  41. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.sha256 +1 -0
  42. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.sha512 +1 -0
  43. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar +0 -0
  44. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.md5 +1 -0
  45. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.sha1 +1 -0
  46. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.sha256 +1 -0
  47. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.sha512 +1 -0
  48. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.11/expo.modules.ui-57.0.11.module → 57.0.13/expo.modules.ui-57.0.13.module} +22 -22
  49. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.md5 +1 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.sha1 +1 -0
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.sha256 +1 -0
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.sha512 +1 -0
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.11/expo.modules.ui-57.0.11.pom → 57.0.13/expo.modules.ui-57.0.13.pom} +1 -1
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.md5 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.sha1 +1 -0
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.sha256 +1 -0
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.sha512 +1 -0
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  62. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  63. package/package.json +3 -3
  64. package/src/jetpack-compose/Image/index.tsx +149 -0
  65. package/src/jetpack-compose/index.ts +1 -0
  66. package/src/swift-ui/Alert/index.tsx +1 -0
  67. package/src/swift-ui/ConfirmationDialog/index.tsx +1 -0
  68. package/src/swift-ui/Overlay/index.tsx +1 -0
  69. package/src/swift-ui/Popover/index.tsx +1 -0
  70. package/src/swift-ui/Slider/index.tsx +14 -8
  71. package/src/swift-ui/modifiers/index.ts +41 -8
  72. package/src/universal/BottomSheet/index.android.tsx +16 -2
  73. package/src/universal/BottomSheet/index.ios.tsx +9 -1
  74. package/src/universal/BottomSheet/index.tsx +17 -2
  75. package/src/universal/BottomSheet/types.ts +50 -0
  76. package/src/universal/BottomSheet/utils.ts +35 -0
  77. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11-sources.jar.md5 +0 -1
  78. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11-sources.jar.sha1 +0 -1
  79. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11-sources.jar.sha256 +0 -1
  80. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11-sources.jar.sha512 +0 -1
  81. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.aar +0 -0
  82. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.aar.md5 +0 -1
  83. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.aar.sha1 +0 -1
  84. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.aar.sha256 +0 -1
  85. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.aar.sha512 +0 -1
  86. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.module.md5 +0 -1
  87. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.module.sha1 +0 -1
  88. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.module.sha256 +0 -1
  89. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.module.sha512 +0 -1
  90. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.pom.md5 +0 -1
  91. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.pom.sha1 +0 -1
  92. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.pom.sha256 +0 -1
  93. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.11/expo.modules.ui-57.0.11.pom.sha512 +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/ui",
3
- "version": "57.0.11",
3
+ "version": "57.0.13",
4
4
  "description": "A collection of UI components",
5
5
  "sideEffects": [
6
6
  "*.fx.js",
@@ -96,7 +96,7 @@
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": "57.0.13",
99
+ "expo": "57.0.16",
100
100
  "expo-module-scripts": "56.0.3"
101
101
  },
102
102
  "jest": {
@@ -121,7 +121,7 @@
121
121
  "optional": true
122
122
  }
123
123
  },
124
- "gitHead": "125b8d4472432db3dc4ae047730432d7bd5bd30c",
124
+ "gitHead": "52fc013ec8c75e89ba8f83c167e4b6b0416e6bf7",
125
125
  "scripts": {
126
126
  "build": "expo-module build",
127
127
  "clean": "expo-module clean",
@@ -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>}
@@ -157,18 +157,26 @@ export const containerRelativeFrame = (params: {
157
157
 
158
158
  /**
159
159
  * Sets padding on a view.
160
- * 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.
161
164
  * @param params - The padding parameters: `top`, `bottom`, `leading`, `trailing`, `horizontal`, `vertical` and `all`.
162
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
+ * ```
163
171
  */
164
172
  export const padding = (params?: {
165
- top?: number;
166
- bottom?: number;
167
- leading?: number;
168
- trailing?: number;
169
- horizontal?: number;
170
- vertical?: number;
171
- 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';
172
180
  }) => createModifier('padding', params);
173
181
 
174
182
  /**
@@ -1121,6 +1129,30 @@ export const listRowSeparatorTint = (color?: Color, edges?: 'all' | 'top' | 'bot
1121
1129
  */
1122
1130
  export const listRowSpacing = (spacing?: number) => createModifier('listRowSpacing', { spacing });
1123
1131
 
1132
+ /**
1133
+ * Sets the position of a horizontal alignment guide of this view.
1134
+ *
1135
+ * Use the `'listRowSeparatorLeading'` guide to set where the separator of a `List` row starts.
1136
+ * SwiftUI insets a row separator past a leading `Image`, but not past other leading content, so
1137
+ * rows with different leading content get separators that start at different offsets.
1138
+ * @param guide - The horizontal alignment guide to set. `'listRowSeparatorLeading'` and
1139
+ * `'listRowSeparatorTrailing'` do nothing on tvOS, where SwiftUI does not provide them.
1140
+ * @param value - The position of the guide, in points from the leading edge of the view.
1141
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/alignmentguide(_:computevalue:)-9mdoh).
1142
+ *
1143
+ * @example
1144
+ * ```tsx
1145
+ * <HStack modifiers={[alignmentGuide('listRowSeparatorLeading', 32)]}>
1146
+ * <Text>A</Text>
1147
+ * <Text>The separator starts 32 points from the leading edge</Text>
1148
+ * </HStack>
1149
+ * ```
1150
+ */
1151
+ export const alignmentGuide = (
1152
+ guide: 'leading' | 'center' | 'trailing' | 'listRowSeparatorLeading' | 'listRowSeparatorTrailing',
1153
+ value: number
1154
+ ) => createModifier('alignmentGuide', { guide, value });
1155
+
1124
1156
  /**
1125
1157
  * Sets the truncation mode for lines of text that are too long to fit in the available space.
1126
1158
  * @param mode - The truncation mode that specifies where to truncate the text within the text view, if needed.
@@ -1735,6 +1767,7 @@ export type BuiltInModifier =
1735
1767
  | ReturnType<typeof listRowSeparator>
1736
1768
  | ReturnType<typeof listRowSeparatorTint>
1737
1769
  | ReturnType<typeof listRowSpacing>
1770
+ | ReturnType<typeof alignmentGuide>
1738
1771
  | ReturnType<typeof truncationMode>
1739
1772
  | ReturnType<typeof allowsTightening>
1740
1773
  | ReturnType<typeof kerning>
@@ -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,10 @@ export function BottomSheet({
38
39
  snapPoints,
39
40
  testID,
40
41
  modifiers,
42
+ shouldDismissOnBackPress = true,
43
+ shouldDismissOnClickOutside = true,
44
+ scrimColor,
45
+ contentPadding,
41
46
  }: BottomSheetProps) {
42
47
  const sheetRef = useRef<ModalBottomSheetRef>(null);
43
48
  const [mount, setMount] = useState(isPresented);
@@ -60,7 +65,15 @@ export function BottomSheet({
60
65
  return null;
61
66
  }
62
67
 
63
- const contentModifiers: ModifierConfig[] = [padding(16, showDragIndicator ? 0 : 16, 16, 0)];
68
+ // When the drag handle is hidden, the default inset adds top padding so content doesn't crop
69
+ // against the top edge of the sheet.
70
+ const { top, bottom, left, right } = resolveContentPadding(contentPadding, {
71
+ top: showDragIndicator ? 0 : 16,
72
+ bottom: 0,
73
+ left: 16,
74
+ right: 16,
75
+ });
76
+ const contentModifiers: ModifierConfig[] = [padding(left, top, right, bottom)];
64
77
  if (shouldFillMaxHeight(snapPoints)) contentModifiers.push(fillMaxHeight());
65
78
  if (testID) contentModifiers.push(testIDModifier(testID));
66
79
 
@@ -71,8 +84,9 @@ export function BottomSheet({
71
84
  onDismissRequest={onDismiss}
72
85
  showDragHandle={showDragIndicator}
73
86
  skipPartiallyExpanded={shouldSkipPartiallyExpanded(snapPoints)}
87
+ properties={{ shouldDismissOnBackPress, shouldDismissOnClickOutside }}
88
+ scrimColor={scrimColor}
74
89
  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
90
  <Column modifiers={contentModifiers}>{children}</Column>
77
91
  </ModalBottomSheet>
78
92
  </Host>
@@ -9,6 +9,7 @@ import {
9
9
  } from '@expo/ui/swift-ui/modifiers';
10
10
 
11
11
  import type { BottomSheetProps, SnapPoint } from './types';
12
+ import { resolveContentPadding } from './utils';
12
13
 
13
14
  function snapPointToDetent(snapPoint: SnapPoint): PresentationDetent {
14
15
  if (snapPoint === 'half') return 'medium';
@@ -24,10 +25,17 @@ export function BottomSheet({
24
25
  snapPoints,
25
26
  testID,
26
27
  modifiers,
28
+ contentPadding,
27
29
  }: BottomSheetProps) {
30
+ const { top, bottom, left, right } = resolveContentPadding(contentPadding, {
31
+ top: 16,
32
+ bottom: 0,
33
+ left: 16,
34
+ right: 16,
35
+ });
28
36
  const presentationModifiers: ModifierConfig[] = [
29
37
  frame({ maxWidth: Infinity, alignment: 'topLeading' }),
30
- padding({ top: 16, leading: 16, trailing: 16 }),
38
+ padding({ top, bottom, leading: left, trailing: right }),
31
39
  presentationDragIndicator(showDragIndicator ? 'visible' : 'hidden'),
32
40
  ];
33
41
  if (snapPoints && snapPoints.length > 0) {
@@ -2,6 +2,7 @@ import { useColorScheme } from 'react-native';
2
2
  import { Drawer } from 'vaul';
3
3
 
4
4
  import type { BottomSheetProps, SnapPoint } from './types';
5
+ import { resolveContentPadding } from './utils';
5
6
 
6
7
  // Visually-hidden style for the screen-reader-only Drawer.Title.
7
8
  const visuallyHiddenStyle: React.CSSProperties = {
@@ -33,8 +34,15 @@ export function BottomSheet({
33
34
  showDragIndicator = true,
34
35
  snapPoints,
35
36
  testID,
37
+ contentPadding,
36
38
  }: BottomSheetProps) {
37
39
  const isDark = useColorScheme() === 'dark';
40
+ const { top, bottom, left, right } = resolveContentPadding(contentPadding, {
41
+ top: 16,
42
+ bottom: 16,
43
+ left: 16,
44
+ right: 16,
45
+ });
38
46
  const vaulSnapPoints = snapPoints?.length ? snapPoints.map(snapPointToVaul) : undefined;
39
47
  const hasSnapPoints = vaulSnapPoints != null;
40
48
 
@@ -60,7 +68,15 @@ export function BottomSheet({
60
68
  {/* Radix Dialog requires a title for a11y; render visually-hidden. */}
61
69
  <Drawer.Title style={visuallyHiddenStyle}>Bottom sheet</Drawer.Title>
62
70
  {showDragIndicator && <Drawer.Handle />}
63
- <div style={innerStyle} data-testid={testID}>
71
+ <div
72
+ style={{
73
+ ...innerStyle,
74
+ paddingTop: top,
75
+ paddingBottom: bottom,
76
+ paddingLeft: left,
77
+ paddingRight: right,
78
+ }}
79
+ data-testid={testID}>
64
80
  {children}
65
81
  </div>
66
82
  </Drawer.Content>
@@ -99,7 +115,6 @@ const noSnapPointContentStyle: React.CSSProperties = {
99
115
  };
100
116
 
101
117
  const innerStyle: React.CSSProperties = {
102
- padding: 16,
103
118
  overflow: 'auto',
104
119
  };
105
120
 
@@ -1,3 +1,5 @@
1
+ import type { ColorValue } from 'react-native';
2
+
1
3
  import type { ModifierConfig } from '../../types';
2
4
 
3
5
  /**
@@ -15,6 +17,19 @@ import type { ModifierConfig } from '../../types';
15
17
  */
16
18
  export type SnapPoint = 'half' | 'full' | { fraction: number } | { height: number };
17
19
 
20
+ /**
21
+ * Padding between a [`BottomSheet`](#bottomsheet) and its content — a single value applied to every
22
+ * edge, or per-edge values where an edge that is left out is `0`.
23
+ */
24
+ export type BottomSheetContentPadding =
25
+ | number
26
+ | {
27
+ top?: number;
28
+ bottom?: number;
29
+ left?: number;
30
+ right?: number;
31
+ };
32
+
18
33
  /**
19
34
  * Props for the [`BottomSheet`](#bottomsheet) component, a modal sheet that slides up from the bottom of the screen.
20
35
  */
@@ -50,11 +65,46 @@ export interface BottomSheetProps {
50
65
  */
51
66
  snapPoints?: SnapPoint[];
52
67
 
68
+ /**
69
+ * Padding between the sheet and [`children`](#children), in dp on Android, points on iOS, and
70
+ * CSS pixels on web. Pass `0` for content that reaches the sheet's edges.
71
+ *
72
+ * When omitted, each platform keeps the inset it applies by default.
73
+ *
74
+ * @example `contentPadding={0}` — full-bleed content
75
+ * @example `contentPadding={{ top: 8, bottom: 24 }}` — no horizontal inset
76
+ */
77
+ contentPadding?: BottomSheetContentPadding;
78
+
53
79
  /**
54
80
  * Identifier used to locate the component in end-to-end tests.
55
81
  */
56
82
  testID?: string;
57
83
 
84
+ /**
85
+ * Whether pressing the Android hardware back button (or back gesture) dismisses the bottom sheet.
86
+ * When `false`, the back press does not dismiss the sheet (note: the press still does not reach
87
+ * React Native's `BackHandler`).
88
+ * @default true
89
+ * @platform android
90
+ */
91
+ shouldDismissOnBackPress?: boolean;
92
+
93
+ /**
94
+ * Whether tapping the backdrop (scrim) dismisses the bottom sheet.
95
+ * When `false`, the sheet stays open until the user explicitly closes it (e.g. via a button).
96
+ * @default true
97
+ * @platform android
98
+ */
99
+ shouldDismissOnClickOutside?: boolean;
100
+
101
+ /**
102
+ * The color of the scrim overlay rendered behind the bottom sheet.
103
+ * Pass `'transparent'` to make the backdrop invisible while still blocking touches.
104
+ * @platform android
105
+ */
106
+ scrimColor?: ColorValue;
107
+
58
108
  /**
59
109
  * Platform-specific modifier escape hatch. Pass an array of modifier configs
60
110
  * from `@expo/ui/swift-ui/modifiers` or `@expo/ui/jetpack-compose/modifiers`.
@@ -0,0 +1,35 @@
1
+ import type { BottomSheetContentPadding } from './types';
2
+
3
+ export type ResolvedContentPadding = {
4
+ top: number;
5
+ bottom: number;
6
+ left: number;
7
+ right: number;
8
+ };
9
+
10
+ /**
11
+ * Resolves the `contentPadding` prop against the inset the platform applies when the prop is
12
+ * omitted. A number applies to every edge, and an edge left out of an object is `0`.
13
+ */
14
+ export function resolveContentPadding(
15
+ contentPadding: BottomSheetContentPadding | undefined,
16
+ platformDefault: ResolvedContentPadding
17
+ ): ResolvedContentPadding {
18
+ if (contentPadding == null) {
19
+ return platformDefault;
20
+ }
21
+ if (typeof contentPadding === 'number') {
22
+ return {
23
+ top: contentPadding,
24
+ bottom: contentPadding,
25
+ left: contentPadding,
26
+ right: contentPadding,
27
+ };
28
+ }
29
+ return {
30
+ top: contentPadding.top ?? 0,
31
+ bottom: contentPadding.bottom ?? 0,
32
+ left: contentPadding.left ?? 0,
33
+ right: contentPadding.right ?? 0,
34
+ };
35
+ }
@@ -1 +0,0 @@
1
- ff3268099478cd30644b24d64f739ca622826811
@@ -1 +0,0 @@
1
- 1c278284d86a0c256802d5c4bc4e8780650313dfca9cd372f11270677138f9fb
@@ -1 +0,0 @@
1
- 938426b2ed24a041a9cc2ab04e9fe12fac0bdf6556b6daadc5f14907395625122c9cb4d77e2aae78780413995cedc71c97d59f003da45a702af6328b0f4312d6
@@ -1 +0,0 @@
1
- 89971894361a24c8a79097da2beeb18acde639c8
@@ -1 +0,0 @@
1
- 36d7de508b797671c51d35fc849b3ccee089a15a6bb8852a335d0af13338c78f
@@ -1 +0,0 @@
1
- aaa731134a933ce7a2606140fc2655b2ed4967ca072ed210cd24192ee1316e2aace1d21d5c63660ee67888cdfddf86092f690a5d54767644eb6a40c9e64713f7
@@ -1 +0,0 @@
1
- 65af783c4afe310e5c6ee00745c0c6e5a239b005
@@ -1 +0,0 @@
1
- bee301bdc67401448d4fa2fad75928fba0f9877e339f3ae2dff2c9b9538450e8
@@ -1 +0,0 @@
1
- f5937b1b9255794c9831ba8c1b17efd5137bdeb4b90ac6f7417aa5ab179abea1339efbf406e3dfd38f05809ff1155e3db27b3bbcbee8a25621455dcbbb171a16
@@ -1 +0,0 @@
1
- 62d838ac8861f8481d98555383b063480b6bbb67
@@ -1 +0,0 @@
1
- 2123d3b3693b5a6bb8a826d69eda11510cd012743a02598e45946923ecf2eea6
@@ -1 +0,0 @@
1
- a6b49c57e6bc62137877d821131c05618d0ece33ff76b6ea3c9f5b19c6dbd25fdd832548892906ded177a2fd7888ceeb3e851ec8e997c98b09e041159de7e5fe