@expo/ui 58.0.14 → 58.0.16

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 (130) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/DatePickerView.kt +35 -2
  4. package/android/src/main/java/expo/modules/ui/DateRangePickerView.kt +35 -2
  5. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +3 -0
  6. package/android/src/main/java/expo/modules/ui/HostView.kt +21 -0
  7. package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +2 -2
  8. package/android/src/main/java/expo/modules/ui/RNHostView.kt +117 -1
  9. package/android/src/main/java/expo/modules/ui/menu/DropdownMenu.kt +8 -1
  10. package/android/src/main/java/expo/modules/ui/menu/DropdownMenuRecords.kt +13 -1
  11. package/build/NativeTreeContext.d.ts +10 -0
  12. package/build/NativeTreeContext.js +25 -0
  13. package/build/NativeTreeContext.js.map +1 -0
  14. package/build/State/useWorkletCallback.d.ts +1 -1
  15. package/build/community/bottom-sheet/BottomSheet.ios.js +11 -4
  16. package/build/community/bottom-sheet/BottomSheet.ios.js.map +1 -1
  17. package/build/community/datetime-picker/DateTimePicker.android.js +4 -0
  18. package/build/community/datetime-picker/DateTimePicker.android.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/DatePicker/index.d.ts +38 -0
  22. package/build/jetpack-compose/DatePicker/index.js.map +1 -1
  23. package/build/jetpack-compose/DropdownMenu/index.d.ts +8 -1
  24. package/build/jetpack-compose/DropdownMenu/index.js.map +1 -1
  25. package/build/jetpack-compose/Host/index.js +8 -2
  26. package/build/jetpack-compose/Host/index.js.map +1 -1
  27. package/build/jetpack-compose/RNHostView/index.js +5 -1
  28. package/build/jetpack-compose/RNHostView/index.js.map +1 -1
  29. package/build/swift-ui/Chart/index.d.ts +10 -0
  30. package/build/swift-ui/Chart/index.js.map +1 -1
  31. package/build/swift-ui/Host/index.js +8 -2
  32. package/build/swift-ui/Host/index.js.map +1 -1
  33. package/build/swift-ui/RNHostView.js +5 -1
  34. package/build/swift-ui/RNHostView.js.map +1 -1
  35. package/build/swift-ui/Toolbar/index.d.ts +2 -0
  36. package/build/swift-ui/Toolbar/index.js +2 -0
  37. package/build/swift-ui/Toolbar/index.js.map +1 -1
  38. package/build/swift-ui/modifiers/hingeObservation.d.ts +1 -1
  39. package/build/swift-ui/modifiers/hingeObservation.js +1 -1
  40. package/build/swift-ui/modifiers/index.d.ts +1 -0
  41. package/build/swift-ui/modifiers/index.js +1 -0
  42. package/build/swift-ui/modifiers/index.js.map +1 -1
  43. package/build/swift-ui/modifiers/presentationModifiers.d.ts +17 -1
  44. package/build/swift-ui/modifiers/presentationModifiers.js +4 -2
  45. package/build/swift-ui/modifiers/presentationModifiers.js.map +1 -1
  46. package/build/swift-ui/modifiers/searchable.d.ts +83 -0
  47. package/build/swift-ui/modifiers/searchable.js +74 -0
  48. package/build/swift-ui/modifiers/searchable.js.map +1 -0
  49. package/build/types.d.ts +27 -0
  50. package/build/types.js +1 -1
  51. package/build/types.js.map +1 -1
  52. package/build/universal/BottomSheet/index.android.js +26 -18
  53. package/build/universal/BottomSheet/index.android.js.map +1 -1
  54. package/build/universal/BottomSheet/index.ios.js +20 -12
  55. package/build/universal/BottomSheet/index.ios.js.map +1 -1
  56. package/ios/ChartView.swift +15 -10
  57. package/ios/DataListForEachView.swift +1 -0
  58. package/ios/GridView.swift +1 -0
  59. package/ios/HostView.swift +27 -4
  60. package/ios/Modifiers/PresentationModifiers.swift +20 -4
  61. package/ios/Modifiers/SearchToolbarBehaviorModifier.swift +35 -0
  62. package/ios/Modifiers/SearchableModifier.swift +94 -0
  63. package/ios/Modifiers/ViewModifierRegistry.swift +8 -0
  64. package/ios/NamespaceView.swift +1 -0
  65. package/ios/Picker/PickerView.swift +9 -7
  66. package/ios/RNHostView.swift +19 -4
  67. package/ios/SlotView.swift +1 -0
  68. package/ios/TabView/TabView.swift +2 -0
  69. package/ios/ToolbarView.swift +2 -1
  70. package/ios/UIBaseView.swift +13 -0
  71. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.14/expo.modules.ui-58.0.14-sources.jar → 58.0.16/expo.modules.ui-58.0.16-sources.jar} +0 -0
  72. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16-sources.jar.md5 +1 -0
  73. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16-sources.jar.sha1 +1 -0
  74. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16-sources.jar.sha256 +1 -0
  75. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16-sources.jar.sha512 +1 -0
  76. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.aar +0 -0
  77. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.aar.md5 +1 -0
  78. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.aar.sha1 +1 -0
  79. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.aar.sha256 +1 -0
  80. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.aar.sha512 +1 -0
  81. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.14/expo.modules.ui-58.0.14.module → 58.0.16/expo.modules.ui-58.0.16.module} +22 -22
  82. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.module.md5 +1 -0
  83. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.module.sha1 +1 -0
  84. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.module.sha256 +1 -0
  85. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.module.sha512 +1 -0
  86. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.14/expo.modules.ui-58.0.14.pom → 58.0.16/expo.modules.ui-58.0.16.pom} +1 -1
  87. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.pom.md5 +1 -0
  88. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.pom.sha1 +1 -0
  89. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.pom.sha256 +1 -0
  90. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.16/expo.modules.ui-58.0.16.pom.sha512 +1 -0
  91. package/package.json +2 -2
  92. package/prebuilds/output/debug/xcframeworks/ExpoUI.tar.gz +0 -0
  93. package/prebuilds/output/release/xcframeworks/ExpoUI.tar.gz +0 -0
  94. package/src/NativeTreeContext.tsx +31 -0
  95. package/src/State/useWorkletCallback.ts +1 -1
  96. package/src/community/bottom-sheet/BottomSheet.ios.tsx +7 -6
  97. package/src/community/datetime-picker/DateTimePicker.android.tsx +4 -0
  98. package/src/community/menu/MenuView.android.tsx +12 -2
  99. package/src/jetpack-compose/DatePicker/index.tsx +38 -0
  100. package/src/jetpack-compose/DropdownMenu/index.tsx +12 -1
  101. package/src/jetpack-compose/Host/index.tsx +6 -2
  102. package/src/jetpack-compose/RNHostView/index.tsx +3 -1
  103. package/src/swift-ui/Chart/index.tsx +10 -0
  104. package/src/swift-ui/Host/index.tsx +6 -2
  105. package/src/swift-ui/RNHostView.tsx +3 -1
  106. package/src/swift-ui/Toolbar/index.tsx +2 -0
  107. package/src/swift-ui/modifiers/hingeObservation.ts +1 -1
  108. package/src/swift-ui/modifiers/index.ts +1 -0
  109. package/src/swift-ui/modifiers/presentationModifiers.ts +18 -2
  110. package/src/swift-ui/modifiers/searchable.ts +107 -0
  111. package/src/types.ts +28 -0
  112. package/src/universal/BottomSheet/index.android.tsx +23 -12
  113. package/src/universal/BottomSheet/index.ios.tsx +20 -9
  114. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14-sources.jar.md5 +0 -1
  115. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14-sources.jar.sha1 +0 -1
  116. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14-sources.jar.sha256 +0 -1
  117. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14-sources.jar.sha512 +0 -1
  118. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.aar +0 -0
  119. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.aar.md5 +0 -1
  120. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.aar.sha1 +0 -1
  121. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.aar.sha256 +0 -1
  122. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.aar.sha512 +0 -1
  123. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.module.md5 +0 -1
  124. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.module.sha1 +0 -1
  125. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.module.sha256 +0 -1
  126. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.module.sha512 +0 -1
  127. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.pom.md5 +0 -1
  128. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.pom.sha1 +0 -1
  129. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.pom.sha256 +0 -1
  130. package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.14/expo.modules.ui-58.0.14.pom.sha512 +0 -1
@@ -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
  );
@@ -96,6 +96,7 @@ function ToolbarContent(props: ToolbarContentProps) {
96
96
  * ```
97
97
  *
98
98
  * @platform ios
99
+ * @platform tvos
99
100
  */
100
101
  export function ToolbarItem({
101
102
  placement = 'automatic',
@@ -125,6 +126,7 @@ export function ToolbarItem({
125
126
  * ```
126
127
  *
127
128
  * @platform ios
129
+ * @platform tvos
128
130
  */
129
131
  function ToolbarComponent(props: ToolbarProps) {
130
132
  const { modifiers, children, ...restProps } = props;
@@ -49,7 +49,7 @@ export type HingeChangeHandler = (oldContext: HingeContext, newContext: HingeCon
49
49
  * state for interactions and effects, not for layout.
50
50
  *
51
51
  * Pass a plain function to run it on the JS thread, or a callback from
52
- * [`useWorkletCallback`](../usenativestate/#useworkletcallback) to run it synchronously on the UI
52
+ * [`useWorkletCallback`](./usenativestate/#useworkletcallbackcallback) to run it synchronously on the UI
53
53
  * thread, which suits driving a native state value from the continuous `angle`.
54
54
  *
55
55
  * The first call happens when the view appears, and its old context has a `null` hinge. On devices
@@ -1999,6 +1999,7 @@ export * from './presentationModifiers';
1999
1999
  export * from './environment';
2000
2000
  export type { ShapeStyle } from './shapeStyle';
2001
2001
  export * from './scrollPosition';
2002
+ export * from './searchable';
2002
2003
  export * from './symbolEffect';
2003
2004
  export * from './scrollObservation';
2004
2005
  export * from './hingeObservation';
@@ -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 });
@@ -0,0 +1,107 @@
1
+ import { getStateId, type ObservableState } from '../../State';
2
+ import { createModifier, createModifierWithEventListener } from './createModifier';
3
+
4
+ /**
5
+ * The placement of a search field in a view hierarchy.
6
+ *
7
+ * - `automatic`: SwiftUI places the search field automatically.
8
+ * - `toolbar`: The search field appears in the toolbar. Falls back to `automatic` on tvOS.
9
+ * - `sidebar`: The search field appears in the sidebar of a navigation view. Falls back to
10
+ * `automatic` on tvOS.
11
+ * - `navigationBarDrawer`: The search field appears in the navigation bar. Falls back to
12
+ * `automatic` on macOS and tvOS.
13
+ */
14
+ export type SearchFieldPlacement = 'automatic' | 'toolbar' | 'sidebar' | 'navigationBarDrawer';
15
+
16
+ /**
17
+ * Marks this view as searchable, which configures the display of a search field.
18
+ *
19
+ * @param text - The text to display and edit in the search field. An `ObservableState<string>`
20
+ * created with `useNativeState`.
21
+ * @param options.placement - The preferred placement of the search field within the containing
22
+ * view hierarchy. Defaults to `automatic`.
23
+ * @param options.prompt - The prompt of the search field, which provides users with guidance on
24
+ * what to search for.
25
+ * @param options.onChange - Fires on the JS thread when the user edits or clears the search field.
26
+ * It does not fire for values written from JavaScript.
27
+ *
28
+ * @platform ios
29
+ * @platform tvos
30
+ * @platform macos
31
+ *
32
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/searchable(text:placement:prompt:)-18a8f).
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * const query = useNativeState('');
37
+ *
38
+ * <Host>
39
+ * <NavigationStack>
40
+ * <List
41
+ * modifiers={[
42
+ * searchable(query, {
43
+ * prompt: 'Search agents',
44
+ * onChange: (text) => setFilter(text),
45
+ * }),
46
+ * ]}>
47
+ * {rows.map((row) => (
48
+ * <Text key={row.id}>{row.name}</Text>
49
+ * ))}
50
+ * </List>
51
+ * </NavigationStack>
52
+ * </Host>
53
+ * ```
54
+ */
55
+ export const searchable = (
56
+ text: ObservableState<string>,
57
+ options?: {
58
+ placement?: SearchFieldPlacement;
59
+ prompt?: string;
60
+ onChange?: (text: string) => void;
61
+ }
62
+ ) => {
63
+ const params = {
64
+ text: getStateId(text),
65
+ placement: options?.placement,
66
+ prompt: options?.prompt,
67
+ };
68
+ const onChange = options?.onChange;
69
+ if (onChange) {
70
+ return createModifierWithEventListener(
71
+ 'searchable',
72
+ (event: { text?: string }) => onChange(event?.text ?? ''),
73
+ params
74
+ );
75
+ }
76
+ return createModifier('searchable', params);
77
+ };
78
+
79
+ /**
80
+ * The behavior of a search field in a toolbar.
81
+ *
82
+ * - `automatic`: The automatic behavior.
83
+ * - `minimize`: A search toolbar behavior that prefers rendering a search field as a button-like
84
+ * control. Falls back to `automatic` on macOS and tvOS.
85
+ */
86
+ export type SearchToolbarBehavior = 'automatic' | 'minimize';
87
+
88
+ /**
89
+ * Configures the behavior for search in the toolbar.
90
+ *
91
+ * On iOS below 26.0, the modifier is a no-op.
92
+ *
93
+ * @param behavior - The search field behavior.
94
+ *
95
+ * @platform ios 26.0+
96
+ * @platform tvos 26.0+
97
+ * @platform macos 26.0+
98
+ *
99
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/searchtoolbarbehavior(_:)).
100
+ *
101
+ * @example
102
+ * ```tsx
103
+ * <List modifiers={[searchable(query), searchToolbarBehavior('minimize')]}>
104
+ * ```
105
+ */
106
+ export const searchToolbarBehavior = (behavior: SearchToolbarBehavior) =>
107
+ createModifier('searchToolbarBehavior', { behavior });
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 +0,0 @@
1
- e587fcde7f1c58c339f95759565034f989a49dcf
@@ -1 +0,0 @@
1
- eee15e7c9f3aeb7b3941ead7ce0e72d8174b0b95964c69ef5a1d5fbf02536fbe
@@ -1 +0,0 @@
1
- 3e6c99d1b6aa74d01d30339d222653a631dae2abf99075e4c25fc171fbb33f9d55bca7daee4d1cd4c6ecdcdc89fb71067de948ac4a91135b6ce38cbb337c52d1
@@ -1 +0,0 @@
1
- 1409db74c61bae87d4830fa0652a822c1ba21d80
@@ -1 +0,0 @@
1
- dc0a8e5a28e1f4f7d722e8706b5858844aa6eac089e89e5f8099374b309bed60
@@ -1 +0,0 @@
1
- ef66aaa30d2d2703c4ff5c36c117ffaf8dd41a2d2a9a8a286224b817f77d20ae2298cd6e0959e1a78ccc3c741cbc286db2e8d5a086b4ed9e11acc6a206207d68
@@ -1 +0,0 @@
1
- bac0bb14d7f8206e4f6fea13dab4cf103839b7f6
@@ -1 +0,0 @@
1
- 9211bc6ae1cb6ca9bae211e99fdfecb0870b0e5dfc1bf31d50552e794cc02abf
@@ -1 +0,0 @@
1
- 250717c1fdfaad128b302fc4d7ce975d8cb26444b84ebb91de16d911d91cef07f40632026edc6fe3653c5ebbb621a90e35c1749b301b9e70961f72428265ae4e
@@ -1 +0,0 @@
1
- 2aa19c59ef78a58437075381e54f80196277f198
@@ -1 +0,0 @@
1
- 190fae0bf2c5e0e704aff6203b831db1bb670bb9d12db1d93e2c382a1aaca0a6
@@ -1 +0,0 @@
1
- b1e6707e0766bbcc90144b50abbb05f9dc5bd5c29bda43a4ba32924d7c930425a8c173d500f1ac3d9b1a3d3ad8fb4667fd848ec1ab58bc9ed4d26bbbb7ca7ee7