@expo/ui 58.0.15 → 58.0.17

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 (115) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/android/build.gradle +6 -6
  3. package/android/src/main/java/expo/modules/ui/CarouselView.kt +2 -1
  4. package/android/src/main/java/expo/modules/ui/DatePickerView.kt +15 -5
  5. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +3 -0
  6. package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +2 -2
  7. package/android/src/main/java/expo/modules/ui/RNHostView.kt +95 -0
  8. package/android/src/main/java/expo/modules/ui/SliderView.kt +21 -9
  9. package/android/src/main/java/expo/modules/ui/ToggleButtonView.kt +1 -1
  10. package/android/src/main/java/expo/modules/ui/VerticalSliderView.kt +23 -14
  11. package/android/src/main/java/expo/modules/ui/menu/DropdownMenu.kt +8 -1
  12. package/android/src/main/java/expo/modules/ui/menu/DropdownMenuRecords.kt +13 -1
  13. package/android/src/main/java/expo/modules/ui/menu/ExposedDropdownMenuView.kt +1 -0
  14. package/build/NativeTreeContext.d.ts +10 -0
  15. package/build/NativeTreeContext.js +25 -0
  16. package/build/NativeTreeContext.js.map +1 -0
  17. package/build/community/bottom-sheet/BottomSheet.ios.js +11 -4
  18. package/build/community/bottom-sheet/BottomSheet.ios.js.map +1 -1
  19. package/build/community/menu/MenuView.android.js +6 -0
  20. package/build/community/menu/MenuView.android.js.map +1 -1
  21. package/build/jetpack-compose/Carousel/index.d.ts +7 -2
  22. package/build/jetpack-compose/Carousel/index.js.map +1 -1
  23. package/build/jetpack-compose/DatePicker/index.d.ts +20 -0
  24. package/build/jetpack-compose/DatePicker/index.js.map +1 -1
  25. package/build/jetpack-compose/DropdownMenu/index.d.ts +8 -1
  26. package/build/jetpack-compose/DropdownMenu/index.js.map +1 -1
  27. package/build/jetpack-compose/Host/index.js +8 -2
  28. package/build/jetpack-compose/Host/index.js.map +1 -1
  29. package/build/jetpack-compose/RNHostView/index.js +5 -1
  30. package/build/jetpack-compose/RNHostView/index.js.map +1 -1
  31. package/build/swift-ui/Chart/index.d.ts +10 -0
  32. package/build/swift-ui/Chart/index.js.map +1 -1
  33. package/build/swift-ui/Host/index.js +8 -2
  34. package/build/swift-ui/Host/index.js.map +1 -1
  35. package/build/swift-ui/RNHostView.js +5 -1
  36. package/build/swift-ui/RNHostView.js.map +1 -1
  37. package/build/swift-ui/modifiers/presentationModifiers.d.ts +17 -1
  38. package/build/swift-ui/modifiers/presentationModifiers.js +4 -2
  39. package/build/swift-ui/modifiers/presentationModifiers.js.map +1 -1
  40. package/build/types.d.ts +27 -0
  41. package/build/types.js +1 -1
  42. package/build/types.js.map +1 -1
  43. package/build/universal/BottomSheet/index.android.js +26 -18
  44. package/build/universal/BottomSheet/index.android.js.map +1 -1
  45. package/build/universal/BottomSheet/index.ios.js +20 -12
  46. package/build/universal/BottomSheet/index.ios.js.map +1 -1
  47. package/build/universal/ListItem/ListItem.android.js +5 -2
  48. package/build/universal/ListItem/ListItem.android.js.map +1 -1
  49. package/ios/ChartView.swift +7 -4
  50. package/ios/DataListForEachView.swift +1 -0
  51. package/ios/GridView.swift +1 -0
  52. package/ios/HostView.swift +27 -4
  53. package/ios/Modifiers/PresentationModifiers.swift +20 -4
  54. package/ios/NamespaceView.swift +1 -0
  55. package/ios/RNHostView.swift +19 -4
  56. package/ios/SlotView.swift +1 -0
  57. package/ios/TabView/TabView.swift +2 -0
  58. package/ios/ToolbarView.swift +2 -1
  59. package/ios/UIBaseView.swift +13 -0
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.15/expo.modules.ui-58.0.15-sources.jar → 58.0.17/expo.modules.ui-58.0.17-sources.jar} +0 -0
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17-sources.jar.md5 +1 -0
  62. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17-sources.jar.sha1 +1 -0
  63. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17-sources.jar.sha256 +1 -0
  64. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17-sources.jar.sha512 +1 -0
  65. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.aar +0 -0
  66. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.aar.md5 +1 -0
  67. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.aar.sha1 +1 -0
  68. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.aar.sha256 +1 -0
  69. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.aar.sha512 +1 -0
  70. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.15/expo.modules.ui-58.0.15.module → 58.0.17/expo.modules.ui-58.0.17.module} +26 -26
  71. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.module.md5 +1 -0
  72. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.module.sha1 +1 -0
  73. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.module.sha256 +1 -0
  74. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.module.sha512 +1 -0
  75. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.15/expo.modules.ui-58.0.15.pom → 58.0.17/expo.modules.ui-58.0.17.pom} +5 -5
  76. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.pom.md5 +1 -0
  77. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.pom.sha1 +1 -0
  78. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.pom.sha256 +1 -0
  79. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.17/expo.modules.ui-58.0.17.pom.sha512 +1 -0
  80. package/package.json +2 -2
  81. package/prebuilds/output/debug/xcframeworks/ExpoUI.tar.gz +0 -0
  82. package/prebuilds/output/release/xcframeworks/ExpoUI.tar.gz +0 -0
  83. package/src/NativeTreeContext.tsx +31 -0
  84. package/src/community/bottom-sheet/BottomSheet.ios.tsx +7 -6
  85. package/src/community/menu/MenuView.android.tsx +12 -2
  86. package/src/jetpack-compose/Carousel/index.tsx +7 -2
  87. package/src/jetpack-compose/DatePicker/index.tsx +20 -0
  88. package/src/jetpack-compose/DropdownMenu/index.tsx +12 -1
  89. package/src/jetpack-compose/Host/index.tsx +6 -2
  90. package/src/jetpack-compose/RNHostView/index.tsx +3 -1
  91. package/src/swift-ui/Chart/index.tsx +10 -0
  92. package/src/swift-ui/Host/index.tsx +6 -2
  93. package/src/swift-ui/RNHostView.tsx +3 -1
  94. package/src/swift-ui/modifiers/presentationModifiers.ts +18 -2
  95. package/src/types.ts +28 -0
  96. package/src/universal/BottomSheet/index.android.tsx +23 -12
  97. package/src/universal/BottomSheet/index.ios.tsx +20 -9
  98. package/src/universal/ListItem/ListItem.android.tsx +7 -2
  99. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15-sources.jar.md5 +0 -1
  100. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15-sources.jar.sha1 +0 -1
  101. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15-sources.jar.sha256 +0 -1
  102. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15-sources.jar.sha512 +0 -1
  103. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.aar +0 -0
  104. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.aar.md5 +0 -1
  105. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.aar.sha1 +0 -1
  106. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.aar.sha256 +0 -1
  107. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.aar.sha512 +0 -1
  108. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.module.md5 +0 -1
  109. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.module.sha1 +0 -1
  110. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.module.sha256 +0 -1
  111. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.module.sha512 +0 -1
  112. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.pom.md5 +0 -1
  113. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.pom.sha1 +0 -1
  114. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.pom.sha256 +0 -1
  115. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.15/expo.modules.ui-58.0.15.pom.sha512 +0 -1
@@ -199,8 +199,9 @@ export function BottomSheet(props: BottomSheetProps) {
199
199
  const bg = extractBackgroundColor(backgroundStyle);
200
200
  return [
201
201
  ...(fitToContents
202
- ? // Makes the iPad sheet size to that content instead of opening near full height.
203
- [presentationSizing('fitted')]
202
+ ? // Fits the sheet height to the content (iPad opens near full height otherwise) and lets
203
+ // the system choose the width.
204
+ [presentationSizing('automatic', { fitted: { horizontal: false, vertical: true } })]
204
205
  : [
205
206
  presentationDetents(detents, {
206
207
  selection: selectedDetent,
@@ -231,16 +232,16 @@ export function BottomSheet(props: BottomSheetProps) {
231
232
  onDismiss={fireCloseCallbacks}
232
233
  fitToContents={fitToContents}>
233
234
  <Group modifiers={modifiers}>
234
- <RNHostView matchContents={fitToContents}>
235
+ <RNHostView matchContents={fitToContents ? { vertical: true } : false}>
235
236
  {/* paddingTop compensates for tighter spacing between native drag indicator and content
236
237
  compared to gorhom's handle. flexGrow:1 + height:0 (flex-basis 0) fills the snap-point
237
238
  height without inheriting the scrollable child's intrinsic content height. With matchContents,
238
- RNHostView lays the hosted view out at its own size, so `width` gives it the sheet width
239
- while its height stays content-sized. */}
239
+ RNHostView takes the width from the sheet, which can be narrower than the window, and
240
+ the height from the content. */}
240
241
  <View
241
242
  style={
242
243
  fitToContents
243
- ? { width, paddingTop: handleComponent !== null ? 16 : 0 }
244
+ ? { paddingTop: handleComponent !== null ? 16 : 0 }
244
245
  : { flexGrow: 1, height: 0, paddingTop: handleComponent !== null ? 16 : 0 }
245
246
  }>
246
247
  <SheetScrollContextReset>{children}</SheetScrollContextReset>
@@ -12,8 +12,12 @@ import { Icon } from '../../jetpack-compose/Icon';
12
12
  import { RNHostView } from '../../jetpack-compose/RNHostView';
13
13
  import { Text as ComposeText } from '../../jetpack-compose/Text';
14
14
  import { useMaterialColors } from '../../jetpack-compose/colors';
15
+ import type { PopupProperties } from '../../types';
15
16
  import type { MenuAction, MenuComponentProps, MenuComponentRef, NativeActionEvent } from './types';
16
17
 
18
+ // Clipped popups drop the status bar offset, so edge-to-edge menus near the bottom detach from the anchor.
19
+ const POPUP_PROPERTIES: PopupProperties = { clippingEnabled: false };
20
+
17
21
  function actionId(action: MenuAction): string {
18
22
  return action.id ?? action.title;
19
23
  }
@@ -82,7 +86,10 @@ function MenuActionItem({ action, onPressAction, dismissAll, destructiveColor }:
82
86
  );
83
87
  }
84
88
  return (
85
- <DropdownMenu expanded={submenuExpanded} onDismissRequest={() => setSubmenuExpanded(false)}>
89
+ <DropdownMenu
90
+ expanded={submenuExpanded}
91
+ onDismissRequest={() => setSubmenuExpanded(false)}
92
+ properties={POPUP_PROPERTIES}>
86
93
  <DropdownMenu.Trigger>
87
94
  <DropdownMenuItem
88
95
  enabled={!attributes?.disabled}
@@ -191,7 +198,10 @@ export function MenuView(props: MenuComponentProps & { ref?: React.Ref<MenuCompo
191
198
  return (
192
199
  <View style={style} testID={testID}>
193
200
  <Host colorScheme={colorScheme} matchContents>
194
- <DropdownMenu expanded={expanded} onDismissRequest={dismissAll}>
201
+ <DropdownMenu
202
+ expanded={expanded}
203
+ onDismissRequest={dismissAll}
204
+ properties={POPUP_PROPERTIES}>
195
205
  <DropdownMenu.Trigger>
196
206
  <RNHostView matchContents>
197
207
  <Pressable
@@ -73,8 +73,13 @@ function createCarouselComponent<P extends { modifiers?: ModifierConfig[] }>(
73
73
 
74
74
  export interface HorizontalCenteredHeroCarouselProps extends CarouselCommonConfig {
75
75
  /**
76
- * Maximum width of the hero item in dp.
77
- * When unspecified, the hero item will be as wide as possible.
76
+ * The width that the large item aims for in dp. The carousel picks the arrangement whose large
77
+ * item width is closest to this value, so the final width can differ.
78
+ * When unspecified, one large item fills the viewport beside two small items.
79
+ */
80
+ preferredItemWidth?: number;
81
+ /**
82
+ * @deprecated Use `preferredItemWidth` instead. The value is now a preferred width, not a maximum.
78
83
  */
79
84
  maxItemWidth?: number;
80
85
  /**
@@ -89,6 +89,10 @@ export type TimePickerElementColors = {
89
89
  /** The color of clock dial numbers when selected or overlapping the selector. */
90
90
  clockDialSelectedContentColor?: ColorValue;
91
91
  /** The color of clock dial numbers when unselected. */
92
+ clockDialContentColor?: ColorValue;
93
+ /**
94
+ * @deprecated Use `clockDialContentColor` instead.
95
+ */
92
96
  clockDialUnselectedContentColor?: ColorValue;
93
97
  /** The color of the clock dial selector (hand). */
94
98
  selectorColor?: ColorValue;
@@ -97,18 +101,34 @@ export type TimePickerElementColors = {
97
101
  /** The background color of the selected AM/PM period. */
98
102
  periodSelectorSelectedContainerColor?: ColorValue;
99
103
  /** The background color of the unselected AM/PM period. */
104
+ periodSelectorContainerColor?: ColorValue;
105
+ /**
106
+ * @deprecated Use `periodSelectorContainerColor` instead.
107
+ */
100
108
  periodSelectorUnselectedContainerColor?: ColorValue;
101
109
  /** The text color of the selected AM/PM period. */
102
110
  periodSelectorSelectedContentColor?: ColorValue;
103
111
  /** The text color of the unselected AM/PM period. */
112
+ periodSelectorContentColor?: ColorValue;
113
+ /**
114
+ * @deprecated Use `periodSelectorContentColor` instead.
115
+ */
104
116
  periodSelectorUnselectedContentColor?: ColorValue;
105
117
  /** The background color of the selected hour/minute segment. */
106
118
  timeSelectorSelectedContainerColor?: ColorValue;
107
119
  /** The background color of the unselected hour/minute segment. */
120
+ timeSelectorContainerColor?: ColorValue;
121
+ /**
122
+ * @deprecated Use `timeSelectorContainerColor` instead.
123
+ */
108
124
  timeSelectorUnselectedContainerColor?: ColorValue;
109
125
  /** The text color of the selected hour/minute segment. */
110
126
  timeSelectorSelectedContentColor?: ColorValue;
111
127
  /** The text color of the unselected hour/minute segment. */
128
+ timeSelectorContentColor?: ColorValue;
129
+ /**
130
+ * @deprecated Use `timeSelectorContentColor` instead.
131
+ */
112
132
  timeSelectorUnselectedContentColor?: ColorValue;
113
133
  };
114
134
 
@@ -3,7 +3,7 @@ import type { ReactNode } from 'react';
3
3
  import type { StyleProp, ViewStyle, ColorValue } from 'react-native';
4
4
 
5
5
  import { PresentedContent } from '../../PresentedContentContext';
6
- import type { ModifierConfig } from '../../types';
6
+ import type { ModifierConfig, PopupProperties } from '../../types';
7
7
  import { createViewModifierEventListener } from '../modifiers/utils';
8
8
 
9
9
  const MenuNativeView: React.ComponentType<NativeMenuProps> = requireNativeView(
@@ -59,6 +59,13 @@ export interface DropdownMenuProps {
59
59
  */
60
60
  shadowElevation?: number;
61
61
 
62
+ /**
63
+ * Properties for the popup window of the dropdown menu.
64
+ *
65
+ * @platform android
66
+ */
67
+ properties?: PopupProperties;
68
+
62
69
  /**
63
70
  * Optional styles to apply to the `DropdownMenu`.
64
71
  */
@@ -76,6 +83,7 @@ type NativeMenuProps = {
76
83
  color?: ColorValue;
77
84
  cornerRadius?: number;
78
85
  shadowElevation?: number;
86
+ properties?: PopupProperties;
79
87
  style?: StyleProp<ViewStyle>;
80
88
  modifiers?: ModifierConfig[];
81
89
  children?: ReactNode;
@@ -129,3 +137,6 @@ DropdownMenu.Items = Items;
129
137
 
130
138
  export { DropdownMenu };
131
139
  export { DropdownMenuItem } from './DropdownMenuItem';
140
+
141
+ // Re-exported so the docs generator includes PopupProperties on the DropdownMenu API page.
142
+ export type { PopupProperties };
@@ -9,6 +9,7 @@ import {
9
9
  useColorScheme as useRNColorScheme,
10
10
  } from 'react-native';
11
11
 
12
+ import { NativeTreeContext, useWarnIfNestedHost } from '../../NativeTreeContext';
12
13
  import { TextInputHostProvider, useTextInputHostRef } from '../../keyboard';
13
14
  import { useMergeRefs } from '../../utils/useMergeRefs';
14
15
  import { getMaterialColors, HostPaletteContext } from '../colors';
@@ -93,6 +94,7 @@ export function Host(props: HostProps) {
93
94
  colorScheme,
94
95
  seedColor,
95
96
  ref,
97
+ children,
96
98
  ...restProps
97
99
  } = props;
98
100
  const schemeString = colorScheme === 'light' || colorScheme === 'dark' ? colorScheme : undefined;
@@ -105,6 +107,7 @@ export function Host(props: HostProps) {
105
107
  );
106
108
  const hostRef = useTextInputHostRef();
107
109
  const mergedRef = useMergeRefs(ref, hostRef);
110
+ useWarnIfNestedHost('Jetpack Compose');
108
111
 
109
112
  return (
110
113
  <HostPaletteContext.Provider value={palette}>
@@ -124,8 +127,9 @@ export function Host(props: HostProps) {
124
127
  layoutDirection={
125
128
  layoutDirection ?? (I18nManager.getConstants().isRTL ? 'rightToLeft' : 'leftToRight')
126
129
  }
127
- ref={mergedRef}
128
- />
130
+ ref={mergedRef}>
131
+ <NativeTreeContext.Provider value>{children}</NativeTreeContext.Provider>
132
+ </HostNativeView>
129
133
  </TextInputHostProvider>
130
134
  </HostPaletteContext.Provider>
131
135
  );
@@ -2,6 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import type { ReactElement, ComponentType } from 'react';
3
3
  import type { LayoutChangeEvent } from 'react-native';
4
4
 
5
+ import { NativeTreeContext } from '../../NativeTreeContext';
5
6
  import { PresentedContentContext, useIsPresentedInOwnWindow } from '../../PresentedContentContext';
6
7
  import type { ModifierConfig } from '../../types';
7
8
  import { resolveMatchContents } from '../../utils/matchContents';
@@ -80,7 +81,8 @@ export function RNHostView(props: RNHostProps) {
80
81
  key={`${nativeProps.matchContentsHorizontal}-${nativeProps.matchContentsVertical}`}>
81
82
  {/* Reset context here so only nearest RNHostView becomes the layout root for its children, and not any other RNHostView above it in the tree. */}
82
83
  <PresentedContentContext.Provider value={false}>
83
- {props.children}
84
+ {/* Hosted children are React Native views again, so a `Host` among them is not nested. */}
85
+ <NativeTreeContext.Provider value={false}>{props.children}</NativeTreeContext.Provider>
84
86
  </PresentedContentContext.Provider>
85
87
  </NativeRNHostView>
86
88
  );
@@ -102,6 +102,16 @@ export type PieChartStyle = {
102
102
  * Space between slices in points.
103
103
  */
104
104
  angularInset?: number;
105
+ /**
106
+ * Corner radius of each slice in points. On a donut, half the ring's thickness gives fully rounded ends.
107
+ * @default 0
108
+ */
109
+ cornerRadius?: number;
110
+ /**
111
+ * Opacity of each slice, from 0 to 1.
112
+ * @default 0.8
113
+ */
114
+ opacity?: number;
105
115
  };
106
116
 
107
117
  /**
@@ -2,6 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import type { Ref } from 'react';
3
3
  import { I18nManager, type ColorValue, type StyleProp, type ViewStyle } from 'react-native';
4
4
 
5
+ import { NativeTreeContext, useWarnIfNestedHost } from '../../NativeTreeContext';
5
6
  import { TextInputHostProvider, useTextInputHostRef } from '../../keyboard';
6
7
  import { useMergeRefs } from '../../utils/useMergeRefs';
7
8
  import { createViewModifierEventListener } from '../modifiers/utils';
@@ -81,10 +82,12 @@ export function Host(props: HostProps) {
81
82
  layoutDirection,
82
83
  seedColor,
83
84
  ref,
85
+ children,
84
86
  ...restProps
85
87
  } = props;
86
88
  const hostRef = useTextInputHostRef();
87
89
  const mergedRef = useMergeRefs(ref, hostRef);
90
+ useWarnIfNestedHost('SwiftUI');
88
91
 
89
92
  return (
90
93
  <TextInputHostProvider hostRef={hostRef}>
@@ -104,8 +107,9 @@ export function Host(props: HostProps) {
104
107
  ignoreSafeArea={ignoreSafeArea}
105
108
  seedColor={seedColor}
106
109
  {...restProps}
107
- ref={mergedRef}
108
- />
110
+ ref={mergedRef}>
111
+ <NativeTreeContext.Provider value>{children}</NativeTreeContext.Provider>
112
+ </HostNativeView>
109
113
  </TextInputHostProvider>
110
114
  );
111
115
  }
@@ -1,6 +1,7 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import type { LayoutChangeEvent } from 'react-native';
3
3
 
4
+ import { NativeTreeContext } from '../NativeTreeContext';
4
5
  import { PresentedContentContext, useIsPresentedInOwnWindow } from '../PresentedContentContext';
5
6
  import { resolveMatchContents } from '../utils/matchContents';
6
7
 
@@ -45,7 +46,8 @@ export function RNHostView({ matchContents, ...props }: RNHostViewProps) {
45
46
  key={`${matchContentsHorizontal}-${matchContentsVertical}`}>
46
47
  {/* Reset context here so only nearest RNHostView becomes the layout root for its children, and not any other RNHostView above it in the tree. */}
47
48
  <PresentedContentContext.Provider value={false}>
48
- {props.children}
49
+ {/* Hosted children are React Native views again, so a `Host` among them is not nested. */}
50
+ <NativeTreeContext.Provider value={false}>{props.children}</NativeTreeContext.Provider>
49
51
  </PresentedContentContext.Provider>
50
52
  </RNHostNativeView>
51
53
  );
@@ -130,12 +130,28 @@ export const interactiveDismissDisabled = (isDisabled: boolean = true) =>
130
130
  */
131
131
  export type PresentationSizingType = 'automatic' | 'fitted' | 'form' | 'page';
132
132
 
133
+ /**
134
+ * Options for the `presentationSizing` modifier.
135
+ */
136
+ export type PresentationSizingOptions = {
137
+ /**
138
+ * The axes on which the sheet fits its content. An axis that is not set keeps the size from
139
+ * `sizing`. For example, `{ horizontal: false, vertical: true }` fits the height to the content
140
+ * and lets the system choose the width.
141
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/presentationsizing/fitted(horizontal:vertical:)).
142
+ */
143
+ fitted?: { horizontal?: boolean; vertical?: boolean };
144
+ };
145
+
133
146
  /**
134
147
  * Sets the sizing of a sheet presentation.
135
148
  * @param sizing - The sizing behavior to apply.
149
+ * @param options - Additional sizing options.
136
150
  * @platform ios 18.0+
137
151
  * @platform tvos 18.0+
138
152
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/presentationsizing(_:)).
139
153
  */
140
- export const presentationSizing = (sizing: PresentationSizingType) =>
141
- createModifier('presentationSizing', { sizing });
154
+ export const presentationSizing = (
155
+ sizing: PresentationSizingType,
156
+ options?: PresentationSizingOptions
157
+ ) => createModifier('presentationSizing', { sizing, ...options });
package/src/types.ts CHANGED
@@ -50,3 +50,31 @@ export type DialogProperties = {
50
50
  */
51
51
  decorFitsSystemWindows?: boolean;
52
52
  };
53
+
54
+ /**
55
+ * Properties for the popup window, matching `PopupProperties` in Compose.
56
+ */
57
+ export type PopupProperties = {
58
+ /**
59
+ * Whether the popup is focusable. When `true`, the popup receives IME events and key presses,
60
+ * such as the back button.
61
+ * @default true
62
+ */
63
+ focusable?: boolean;
64
+ /**
65
+ * Whether the popup can be dismissed by pressing the back button. Has no effect when `focusable` is `false`.
66
+ * @default true
67
+ */
68
+ dismissOnBackPress?: boolean;
69
+ /**
70
+ * Whether the popup can be dismissed by clicking outside of its bounds.
71
+ * @default true
72
+ */
73
+ dismissOnClickOutside?: boolean;
74
+ /**
75
+ * Whether the popup window is clipped to the screen boundaries. Set to `false` to let it extend
76
+ * beyond them, which positions the popup accurately.
77
+ * @default true
78
+ */
79
+ clippingEnabled?: boolean;
80
+ };
@@ -7,6 +7,7 @@ import {
7
7
  } from '@expo/ui/jetpack-compose/modifiers';
8
8
  import { useEffect, useRef, useState } from 'react';
9
9
 
10
+ import { useIsInsideNativeTree } from '../../NativeTreeContext';
10
11
  import type { BottomSheetProps, SnapPoint } from './types';
11
12
  import { resolveContentPadding } from './utils';
12
13
 
@@ -46,6 +47,7 @@ export function BottomSheet({
46
47
  containerColor,
47
48
  contentColor,
48
49
  }: BottomSheetProps) {
50
+ const isInsideHost = useIsInsideNativeTree();
49
51
  const sheetRef = useRef<ModalBottomSheetRef>(null);
50
52
  const [mount, setMount] = useState(isPresented);
51
53
 
@@ -79,20 +81,29 @@ export function BottomSheet({
79
81
  if (shouldFillMaxHeight(snapPoints)) contentModifiers.push(fillMaxHeight());
80
82
  if (testID) contentModifiers.push(testIDModifier(testID));
81
83
 
84
+ const sheet = (
85
+ <ModalBottomSheet
86
+ ref={sheetRef}
87
+ onDismissRequest={onDismiss}
88
+ showDragHandle={showDragIndicator}
89
+ skipPartiallyExpanded={shouldSkipPartiallyExpanded(snapPoints)}
90
+ properties={{ shouldDismissOnBackPress, shouldDismissOnClickOutside }}
91
+ scrimColor={scrimColor}
92
+ containerColor={containerColor}
93
+ contentColor={contentColor}
94
+ modifiers={modifiers}>
95
+ <Column modifiers={contentModifiers}>{children}</Column>
96
+ </ModalBottomSheet>
97
+ );
98
+
99
+ // Already inside a `Host`, for example nested in another sheet's content: a `Host` of its own
100
+ // would be a `Host` directly inside Compose.
101
+ if (isInsideHost) {
102
+ return sheet;
103
+ }
82
104
  return (
83
105
  <Host style={{ position: 'absolute' }} pointerEvents="none">
84
- <ModalBottomSheet
85
- ref={sheetRef}
86
- onDismissRequest={onDismiss}
87
- showDragHandle={showDragIndicator}
88
- skipPartiallyExpanded={shouldSkipPartiallyExpanded(snapPoints)}
89
- properties={{ shouldDismissOnBackPress, shouldDismissOnClickOutside }}
90
- scrimColor={scrimColor}
91
- containerColor={containerColor}
92
- contentColor={contentColor}
93
- modifiers={modifiers}>
94
- <Column modifiers={contentModifiers}>{children}</Column>
95
- </ModalBottomSheet>
106
+ {sheet}
96
107
  </Host>
97
108
  );
98
109
  }
@@ -9,6 +9,7 @@ import {
9
9
  type PresentationDetent,
10
10
  } from '@expo/ui/swift-ui/modifiers';
11
11
 
12
+ import { useIsInsideNativeTree } from '../../NativeTreeContext';
12
13
  import type { BottomSheetProps, SnapPoint } from './types';
13
14
  import { resolveContentPadding } from './utils';
14
15
 
@@ -29,6 +30,7 @@ export function BottomSheet({
29
30
  contentPadding,
30
31
  containerColor,
31
32
  }: BottomSheetProps) {
33
+ const isInsideHost = useIsInsideNativeTree();
32
34
  const { top, bottom, left, right } = resolveContentPadding(contentPadding, {
33
35
  top: 16,
34
36
  bottom: 0,
@@ -50,17 +52,26 @@ export function BottomSheet({
50
52
  presentationModifiers.push(...modifiers);
51
53
  }
52
54
 
55
+ const sheet = (
56
+ <SwiftUIBottomSheet
57
+ isPresented={isPresented}
58
+ onIsPresentedChange={(presented) => {
59
+ if (!presented) onDismiss();
60
+ }}
61
+ fitToContents={!snapPoints || snapPoints.length === 0}
62
+ testID={testID}>
63
+ <Group modifiers={presentationModifiers}>{children}</Group>
64
+ </SwiftUIBottomSheet>
65
+ );
66
+
67
+ // Already inside a `Host`, for example nested in another sheet's content: a `Host` of its own
68
+ // would be a `Host` directly inside SwiftUI.
69
+ if (isInsideHost) {
70
+ return sheet;
71
+ }
53
72
  return (
54
73
  <Host style={{ position: 'absolute' }} pointerEvents="none">
55
- <SwiftUIBottomSheet
56
- isPresented={isPresented}
57
- onIsPresentedChange={(presented) => {
58
- if (!presented) onDismiss();
59
- }}
60
- fitToContents={!snapPoints || snapPoints.length === 0}
61
- testID={testID}>
62
- <Group modifiers={presentationModifiers}>{children}</Group>
63
- </SwiftUIBottomSheet>
74
+ {sheet}
64
75
  </Host>
65
76
  );
66
77
  }
@@ -1,5 +1,5 @@
1
1
  import { ListItem as ComposeListItem, Text } from '@expo/ui/jetpack-compose';
2
- import { clickable } from '@expo/ui/jetpack-compose/modifiers';
2
+ import { clickable, testID as testIDModifier } from '@expo/ui/jetpack-compose/modifiers';
3
3
  import { Children, type ReactNode } from 'react';
4
4
 
5
5
  import { extractListItemSlots } from './ListItemSlots';
@@ -25,6 +25,7 @@ export function ListItem(props: ListItemProps) {
25
25
  leading: leadingProp,
26
26
  trailing: trailingProp,
27
27
  supportingText,
28
+ testID,
28
29
  modifiers,
29
30
  colors,
30
31
  } = props;
@@ -32,7 +33,11 @@ export function ListItem(props: ListItemProps) {
32
33
  const leading = slots.leading ?? leadingProp;
33
34
  const trailing = slots.trailing ?? trailingProp;
34
35
  const supporting = slots.supporting ?? supportingText;
35
- const itemModifiers = [...(onPress ? [clickable(onPress)] : []), ...(modifiers ?? [])];
36
+ const itemModifiers = [
37
+ ...(onPress ? [clickable(onPress)] : []),
38
+ ...(testID ? [testIDModifier(testID)] : []),
39
+ ...(modifiers ?? []),
40
+ ];
36
41
 
37
42
  return (
38
43
  <ComposeListItem colors={colors} modifiers={itemModifiers.length ? itemModifiers : undefined}>
@@ -1 +0,0 @@
1
- 584c7a2ab4d40b05b4658c93c1a689ce0587dc06
@@ -1 +0,0 @@
1
- 651218fb79e50f51035a06d7e88ef8cd9cd391f8b66edcf46abdc431f0a41cfc
@@ -1 +0,0 @@
1
- 5569d8f73d7b0d41af8d77ca8cb4e8685384113a45e151b9c6cdad1b660844183067026b2fc16c150ac409a0a097fb4ac78c4f5c31afe56862f306f8daf7c538
@@ -1 +0,0 @@
1
- 019221c48e7c0648984cb5603c4927d391a241cd
@@ -1 +0,0 @@
1
- 85ad13c4695409acef3dad5a8fd8cb6cbd472bcf2ac7d6514b3f0e9fd3b3d590
@@ -1 +0,0 @@
1
- a96d874afa65f7edf884a6b9ea0d01b7d2eb2f1aeb86db4afaaeefa6073f153f29cafe8a1ce8ae9c8c6069cfe8534197add3dba900e350e381598cf3e76e8c79
@@ -1 +0,0 @@
1
- 1f0478b4c8bf3bc52458f429fc59e10278b2ac2c
@@ -1 +0,0 @@
1
- baecdb10cef2e99a106a69d8d1755408faeb3f7f7442966d5e3dbcbfe6e26674
@@ -1 +0,0 @@
1
- 85d71daab274f7e80382dda25bb4fb673bb5d058b58b2f8c6e28cf39f4ff349c9809182bfce05a5395311f2ddf0dd31a830372c49445aaca933908c1327c91ca
@@ -1 +0,0 @@
1
- a9a1e9a3be5d9cd7679f76f857b875155c2bb756
@@ -1 +0,0 @@
1
- 64dbcdae5d503a5245fae1cd35a5a4821daa772a7010e1b76b2fc5bc7fbc5b16
@@ -1 +0,0 @@
1
- 074cbafb601c2b44e22ddc7b09f878b4b98ea71a877ff50c93eab6f7d9c8c18d7629b7fb5e044f9f4d61ef7599e06e9ffdb9d312bae0c4e6d8031eefbfaee45f