@expo/ui 57.0.13 → 57.0.14

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 (119) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +17 -3
  4. package/android/src/main/java/expo/modules/ui/ProgressView.kt +31 -0
  5. package/android/src/main/java/expo/modules/ui/RNHostView.kt +141 -11
  6. package/build/PresentedContentContext.d.ts +18 -0
  7. package/build/PresentedContentContext.d.ts.map +1 -0
  8. package/build/community/pager-view/PagerView.android.d.ts.map +1 -1
  9. package/build/jetpack-compose/AlertDialog/index.d.ts.map +1 -1
  10. package/build/jetpack-compose/BasicAlertDialog/index.d.ts.map +1 -1
  11. package/build/jetpack-compose/DropdownMenu/index.d.ts.map +1 -1
  12. package/build/jetpack-compose/ExposedDropdownMenuBox/ExposedDropdownMenu.d.ts.map +1 -1
  13. package/build/jetpack-compose/Host/index.d.ts +3 -0
  14. package/build/jetpack-compose/Host/index.d.ts.map +1 -1
  15. package/build/jetpack-compose/ModalBottomSheet/index.d.ts.map +1 -1
  16. package/build/jetpack-compose/Progress/index.d.ts +25 -2
  17. package/build/jetpack-compose/Progress/index.d.ts.map +1 -1
  18. package/build/jetpack-compose/RNHostView/index.d.ts +0 -6
  19. package/build/jetpack-compose/RNHostView/index.d.ts.map +1 -1
  20. package/build/jetpack-compose/TextField/shared.d.ts +2 -1
  21. package/build/jetpack-compose/TextField/shared.d.ts.map +1 -1
  22. package/build/jetpack-compose/Tooltip/index.d.ts.map +1 -1
  23. package/build/keyboard/index.d.ts +43 -0
  24. package/build/keyboard/index.d.ts.map +1 -0
  25. package/build/keyboard/interop.d.ts +16 -0
  26. package/build/keyboard/interop.d.ts.map +1 -0
  27. package/build/keyboard/textInputState.d.ts +2 -0
  28. package/build/keyboard/textInputState.d.ts.map +1 -0
  29. package/build/keyboard/textInputState.web.d.ts +11 -0
  30. package/build/keyboard/textInputState.web.d.ts.map +1 -0
  31. package/build/swift-ui/Background/index.d.ts +18 -0
  32. package/build/swift-ui/Background/index.d.ts.map +1 -0
  33. package/build/swift-ui/BottomSheet/index.d.ts.map +1 -1
  34. package/build/swift-ui/Host/index.d.ts +3 -0
  35. package/build/swift-ui/Host/index.d.ts.map +1 -1
  36. package/build/swift-ui/Popover/index.d.ts.map +1 -1
  37. package/build/swift-ui/RNHostView.d.ts +2 -1
  38. package/build/swift-ui/RNHostView.d.ts.map +1 -1
  39. package/build/swift-ui/SecureField/index.d.ts.map +1 -1
  40. package/build/swift-ui/TextField/index.d.ts.map +1 -1
  41. package/build/swift-ui/index.d.ts +1 -0
  42. package/build/swift-ui/index.d.ts.map +1 -1
  43. package/build/utils/useMergeRefs.d.ts +12 -0
  44. package/build/utils/useMergeRefs.d.ts.map +1 -0
  45. package/expo-module.config.json +1 -1
  46. package/ios/BackgroundView.swift +34 -0
  47. package/ios/ColorPickerView.swift +1 -1
  48. package/ios/ExpoUIModule.swift +4 -0
  49. package/ios/HostView.swift +7 -0
  50. package/ios/RNHostView.swift +60 -4
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.13/expo.modules.ui-57.0.13-sources.jar → 57.0.14/expo.modules.ui-57.0.14-sources.jar} +0 -0
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14-sources.jar.md5 +1 -0
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14-sources.jar.sha1 +1 -0
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14-sources.jar.sha256 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14-sources.jar.sha512 +1 -0
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.aar +0 -0
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.aar.md5 +1 -0
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.aar.sha1 +1 -0
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.aar.sha256 +1 -0
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.aar.sha512 +1 -0
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.13/expo.modules.ui-57.0.13.module → 57.0.14/expo.modules.ui-57.0.14.module} +22 -22
  62. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.module.md5 +1 -0
  63. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.module.sha1 +1 -0
  64. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.module.sha256 +1 -0
  65. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.module.sha512 +1 -0
  66. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.13/expo.modules.ui-57.0.13.pom → 57.0.14/expo.modules.ui-57.0.14.pom} +1 -1
  67. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.pom.md5 +1 -0
  68. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.pom.sha1 +1 -0
  69. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.pom.sha256 +1 -0
  70. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.14/expo.modules.ui-57.0.14.pom.sha512 +1 -0
  71. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  72. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  73. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  74. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  75. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  76. package/package.json +4 -4
  77. package/src/PresentedContentContext.tsx +22 -0
  78. package/src/community/pager-view/PagerView.android.tsx +1 -7
  79. package/src/jetpack-compose/AlertDialog/index.tsx +6 -1
  80. package/src/jetpack-compose/BasicAlertDialog/index.tsx +7 -1
  81. package/src/jetpack-compose/DropdownMenu/index.tsx +7 -1
  82. package/src/jetpack-compose/ExposedDropdownMenuBox/ExposedDropdownMenu.tsx +3 -1
  83. package/src/jetpack-compose/Host/index.tsx +28 -17
  84. package/src/jetpack-compose/ModalBottomSheet/index.tsx +8 -1
  85. package/src/jetpack-compose/Progress/index.tsx +32 -2
  86. package/src/jetpack-compose/RNHostView/index.tsx +16 -13
  87. package/src/jetpack-compose/TextField/shared.ts +7 -1
  88. package/src/jetpack-compose/Tooltip/index.tsx +2 -1
  89. package/src/keyboard/index.tsx +243 -0
  90. package/src/keyboard/interop.ts +99 -0
  91. package/src/keyboard/textInputState.ts +2 -0
  92. package/src/keyboard/textInputState.web.ts +16 -0
  93. package/src/swift-ui/Background/index.tsx +38 -0
  94. package/src/swift-ui/BottomSheet/index.tsx +4 -1
  95. package/src/swift-ui/Host/index.tsx +33 -18
  96. package/src/swift-ui/Popover/index.tsx +7 -1
  97. package/src/swift-ui/RNHostView.tsx +17 -3
  98. package/src/swift-ui/SecureField/index.tsx +8 -2
  99. package/src/swift-ui/TextField/index.tsx +8 -1
  100. package/src/swift-ui/index.tsx +1 -0
  101. package/src/ts-declarations/react-native-text-input-state.d.ts +26 -0
  102. package/src/utils/useMergeRefs.ts +47 -0
  103. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.md5 +0 -1
  104. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.sha1 +0 -1
  105. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.sha256 +0 -1
  106. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13-sources.jar.sha512 +0 -1
  107. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar +0 -0
  108. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.md5 +0 -1
  109. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.sha1 +0 -1
  110. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.sha256 +0 -1
  111. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.aar.sha512 +0 -1
  112. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.md5 +0 -1
  113. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.sha1 +0 -1
  114. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.sha256 +0 -1
  115. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.module.sha512 +0 -1
  116. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.md5 +0 -1
  117. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.sha1 +0 -1
  118. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.sha256 +0 -1
  119. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.13/expo.modules.ui-57.0.13.pom.sha512 +0 -1
@@ -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.13</latest>
7
- <release>57.0.13</release>
6
+ <latest>57.0.14</latest>
7
+ <release>57.0.14</release>
8
8
  <versions>
9
- <version>57.0.13</version>
9
+ <version>57.0.14</version>
10
10
  </versions>
11
- <lastUpdated>20260824074615</lastUpdated>
11
+ <lastUpdated>20260826195105</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- c5687c59b58e66b61d7ad8422409a52b
1
+ 4dfe61c00978e656bb86f7a4aba692dd
@@ -1 +1 @@
1
- 19e37e7626a16f1bed2cb5e7e532fe5b2849156f
1
+ b9406185faafd8465b9a6d8c48ab5f7f70546764
@@ -1 +1 @@
1
- 53945cd66292791187c0bc9fab8427ab497726954b09a946a1b4324dc85ac04f
1
+ 39b0c675409d452116a0df7c42ac801510989b59056cbac7611145c7e2548593
@@ -1 +1 @@
1
- e3b22c7838747c4ad21b1a89b65060b12b1511e30770682da53e9056895af8cbad35096654e090e29bc57b794de2f6b0390f70db5c4d3b75187edbbf36f321cb
1
+ de8e79434bbdd397d0eba8e9679e265eb3f2b388de06555138c1a2c9e1f337b9ef0240ba566686cba658d7a7b501523141df379c83d1b35a44f0b6b94210046a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/ui",
3
- "version": "57.0.13",
3
+ "version": "57.0.14",
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": "57.0.16",
100
- "expo-module-scripts": "56.0.3"
99
+ "expo-module-scripts": "56.0.3",
100
+ "expo": "57.0.17"
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": "52fc013ec8c75e89ba8f83c167e4b6b0416e6bf7",
124
+ "gitHead": "c300d2cc60c9e684e64f48d9bc90ea18a571d01d",
125
125
  "scripts": {
126
126
  "build": "expo-module build",
127
127
  "clean": "expo-module clean",
@@ -0,0 +1,22 @@
1
+ import { createContext, useContext, type ReactNode } from 'react';
2
+
3
+ export const PresentedContentContext = createContext(false);
4
+
5
+ /**
6
+ * Whether the surrounding content is presented in its own view controller — a sheet, a popover
7
+ * — rather than in the React Native surface.
8
+ *
9
+ * `RNHostView` needs this because nothing above such content dispatches React Native touches: the
10
+ * surface root is not one of its ancestors. A hosted view there has to dispatch its own touches and
11
+ * be the origin its content is measured from, and both of those come from a single `layoutRoot`
12
+ * prop.
13
+ *
14
+ * Defaults to `false`, so content that is not wrapped keeps deferring to the surface root.
15
+ */
16
+ export function useIsPresentedInOwnWindow(): boolean {
17
+ return useContext(PresentedContentContext);
18
+ }
19
+
20
+ export function PresentedContent({ children }: { children: ReactNode }) {
21
+ return <PresentedContentContext.Provider value>{children}</PresentedContentContext.Provider>;
22
+ }
@@ -77,13 +77,7 @@ export function PagerView(props: PagerViewProps) {
77
77
  const pages = Children.toArray(children)
78
78
  .filter((child): child is ReactElement => isValidElement(child))
79
79
  .map((child, index) => (
80
- <RNHostView
81
- key={child.key ?? String(index)}
82
- // Overlap pages at one origin so each page's RN shadow position matches
83
- // where Compose draws it; otherwise `measure()`-based hit-testing
84
- // (e.g. `Pressable`) misfires on pages after the first (#46386).
85
- style={StyleSheet.absoluteFill}
86
- modifiers={[fillMaxSize()]}>
80
+ <RNHostView key={child.key ?? String(index)} modifiers={[fillMaxSize()]}>
87
81
  <View style={styles.page} pointerEvents={index === settledPage ? 'auto' : 'none'}>
88
82
  {child}
89
83
  </View>
@@ -1,6 +1,7 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import { type ColorValue } from 'react-native';
3
3
 
4
+ import { PresentedContent } from '../../PresentedContentContext';
4
5
  import { type ViewEvent, type ModifierConfig, type DialogProperties } from '../../types';
5
6
  import { createViewModifierEventListener } from '../modifiers/utils';
6
7
 
@@ -123,7 +124,11 @@ function AlertDialogIcon(props: { children: React.ReactNode }) {
123
124
  */
124
125
  function AlertDialogComponent(props: AlertDialogProps) {
125
126
  const { children, ...restProps } = props;
126
- return <AlertDialogNativeView {...transformProps(restProps)}>{children}</AlertDialogNativeView>;
127
+ return (
128
+ <AlertDialogNativeView {...transformProps(restProps)}>
129
+ <PresentedContent>{children}</PresentedContent>
130
+ </AlertDialogNativeView>
131
+ );
127
132
  }
128
133
 
129
134
  AlertDialogComponent.Title = AlertDialogTitle;
@@ -1,5 +1,6 @@
1
1
  import { requireNativeView } from 'expo';
2
2
 
3
+ import { PresentedContent } from '../../PresentedContentContext';
3
4
  import { type ViewEvent, type ModifierConfig, type DialogProperties } from '../../types';
4
5
  import { createViewModifierEventListener } from '../modifiers/utils';
5
6
 
@@ -51,7 +52,12 @@ function transformProps(props: BasicAlertDialogProps): NativeBasicAlertDialogPro
51
52
  * Unlike `AlertDialog`, this component does not have structured title/text/buttons slots.
52
53
  */
53
54
  export function BasicAlertDialog(props: BasicAlertDialogProps) {
54
- return <BasicAlertDialogNativeView {...transformProps(props)} />;
55
+ const { children, ...rest } = props;
56
+ return (
57
+ <BasicAlertDialogNativeView {...transformProps(rest as BasicAlertDialogProps)}>
58
+ <PresentedContent>{children}</PresentedContent>
59
+ </BasicAlertDialogNativeView>
60
+ );
55
61
  }
56
62
 
57
63
  // Re-exported so the docs generator includes DialogProperties on the BasicAlertDialog API page.
@@ -2,6 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { StyleProp, ViewStyle, ColorValue } from 'react-native';
4
4
 
5
+ import { PresentedContent } from '../../PresentedContentContext';
5
6
  import type { ModifierConfig } from '../../types';
6
7
  import { createViewModifierEventListener } from '../modifiers/utils';
7
8
 
@@ -67,7 +68,12 @@ type NativeMenuProps = {
67
68
  * Children should be `DropdownMenuItem` components or other native views.
68
69
  */
69
70
  export function Items(props: { children: ReactNode }) {
70
- return <SlotNativeView slotName="items">{props.children}</SlotNativeView>;
71
+ // The items are presented in their own popup window; the trigger stays in the React Native surface.
72
+ return (
73
+ <SlotNativeView slotName="items">
74
+ <PresentedContent>{props.children}</PresentedContent>
75
+ </SlotNativeView>
76
+ );
71
77
  }
72
78
  Items.tag = 'Items';
73
79
 
@@ -2,6 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import type { ReactNode } from 'react';
3
3
  import { type ColorValue } from 'react-native';
4
4
 
5
+ import { PresentedContent } from '../../PresentedContentContext';
5
6
  import { type ModifierConfig } from '../../types';
6
7
  import { createViewModifierEventListener } from '../modifiers/utils';
7
8
 
@@ -48,7 +49,8 @@ export function ExposedDropdownMenu(props: ExposedDropdownMenuProps) {
48
49
  modifiers={modifiers}
49
50
  {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
50
51
  {...restProps}>
51
- {children}
52
+ {/* The menu is a popup in its own window. The anchor stays in `ExposedDropdownMenuBox`. */}
53
+ <PresentedContent>{children}</PresentedContent>
52
54
  </NativeView>
53
55
  );
54
56
  }
@@ -1,5 +1,5 @@
1
1
  import { requireNativeView } from 'expo';
2
- import { useMemo } from 'react';
2
+ import { useMemo, type Ref } from 'react';
3
3
  import {
4
4
  type ColorSchemeName,
5
5
  type ColorValue,
@@ -9,6 +9,8 @@ import {
9
9
  useColorScheme as useRNColorScheme,
10
10
  } from 'react-native';
11
11
 
12
+ import { TextInputHostProvider, useTextInputHostRef } from '../../keyboard';
13
+ import { useMergeRefs } from '../../utils/useMergeRefs';
12
14
  import { getMaterialColors, HostPaletteContext } from '../colors';
13
15
  import { type PrimitiveBaseProps } from '../layout';
14
16
 
@@ -65,6 +67,8 @@ export interface HostProps extends PrimitiveBaseProps {
65
67
  children: React.ReactNode;
66
68
  style?: StyleProp<ViewStyle>;
67
69
  pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto';
70
+ /** @hidden */
71
+ ref?: Ref<any>;
68
72
  }
69
73
 
70
74
  type NativeHostProps = Omit<HostProps, 'colorScheme'> & {
@@ -72,6 +76,7 @@ type NativeHostProps = Omit<HostProps, 'colorScheme'> & {
72
76
  matchContentsHorizontal?: boolean;
73
77
  colorScheme?: ColorSchemeName;
74
78
  seedColor?: ColorValue;
79
+ ref?: Ref<any>;
75
80
  };
76
81
 
77
82
  const HostNativeView: React.ComponentType<NativeHostProps> = requireNativeView(
@@ -87,6 +92,7 @@ export function Host(props: HostProps) {
87
92
  layoutDirection,
88
93
  colorScheme,
89
94
  seedColor,
95
+ ref,
90
96
  ...restProps
91
97
  } = props;
92
98
  const schemeString = colorScheme === 'light' || colorScheme === 'dark' ? colorScheme : undefined;
@@ -97,25 +103,30 @@ export function Host(props: HostProps) {
97
103
  () => getMaterialColors({ scheme: resolvedScheme, seedColor }),
98
104
  [resolvedScheme, seedColor]
99
105
  );
106
+ const hostRef = useTextInputHostRef();
107
+ const mergedRef = useMergeRefs(ref, hostRef);
100
108
 
101
109
  return (
102
110
  <HostPaletteContext.Provider value={palette}>
103
- <HostNativeView
104
- {...restProps}
105
- modifiers={modifiers}
106
- matchContentsVertical={
107
- typeof matchContents === 'object' ? matchContents.vertical : matchContents
108
- }
109
- matchContentsHorizontal={
110
- typeof matchContents === 'object' ? matchContents.horizontal : matchContents
111
- }
112
- colorScheme={schemeString}
113
- seedColor={seedColor}
114
- onLayoutContent={onLayoutContent}
115
- layoutDirection={
116
- layoutDirection ?? (I18nManager.getConstants().isRTL ? 'rightToLeft' : 'leftToRight')
117
- }
118
- />
111
+ <TextInputHostProvider hostRef={hostRef}>
112
+ <HostNativeView
113
+ {...restProps}
114
+ modifiers={modifiers}
115
+ matchContentsVertical={
116
+ typeof matchContents === 'object' ? matchContents.vertical : matchContents
117
+ }
118
+ matchContentsHorizontal={
119
+ typeof matchContents === 'object' ? matchContents.horizontal : matchContents
120
+ }
121
+ colorScheme={schemeString}
122
+ seedColor={seedColor}
123
+ onLayoutContent={onLayoutContent}
124
+ layoutDirection={
125
+ layoutDirection ?? (I18nManager.getConstants().isRTL ? 'rightToLeft' : 'leftToRight')
126
+ }
127
+ ref={mergedRef}
128
+ />
129
+ </TextInputHostProvider>
119
130
  </HostPaletteContext.Provider>
120
131
  );
121
132
  }
@@ -2,6 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import type { Ref, ReactNode, ComponentType } from 'react';
3
3
  import type { ColorValue } from 'react-native';
4
4
 
5
+ import { PresentedContent } from '../../PresentedContentContext';
5
6
  import { type ModifierConfig } from '../../types';
6
7
  import { createViewModifierEventListener } from '../modifiers/utils';
7
8
 
@@ -138,7 +139,13 @@ function DragHandle(props: { children: ReactNode }) {
138
139
  * A Material Design modal bottom sheet.
139
140
  */
140
141
  function ModalBottomSheetComponent(props: ModalBottomSheetProps) {
141
- return <ModalBottomSheetNativeView {...transformProps(props)} />;
142
+ const { children, ...rest } = props;
143
+ // The whole sheet, drag handle included, is presented in its own window, so all of it is marked.
144
+ return (
145
+ <ModalBottomSheetNativeView {...transformProps(rest as ModalBottomSheetProps)}>
146
+ <PresentedContent>{children}</PresentedContent>
147
+ </ModalBottomSheetNativeView>
148
+ );
142
149
  }
143
150
 
144
151
  ModalBottomSheetComponent.DragHandle = DragHandle;
@@ -128,9 +128,38 @@ export const CircularProgressIndicator = createProgressComponent<CircularProgres
128
128
 
129
129
  // endregion
130
130
 
131
+ // region WavyProgressIndicators
132
+
133
+ /**
134
+ * Wave configuration shared by the wavy progress indicator variants.
135
+ */
136
+ export type WavyProgressCommonConfig = {
137
+ /**
138
+ * Height of the wave, where `0` draws a flat line and `1` draws the full wave height.
139
+ * When set, the amplitude stays the same across the whole track.
140
+ * When omitted, a determinate indicator uses the Material 3 default, which flattens the wave as
141
+ * progress approaches `0` and `1`.
142
+ * An indeterminate indicator defaults to the full wave height.
143
+ */
144
+ amplitude?: number;
145
+ /**
146
+ * Length of a single wave in dp. `CircularWavyProgressIndicator` rounds this to fit a whole
147
+ * number of waves around the ring, so the wave it draws can be slightly shorter or longer.
148
+ */
149
+ wavelength?: number;
150
+ /**
151
+ * Speed of the wave in dp per second. Defaults to `wavelength`, which advances the wave by one
152
+ * wavelength per second. Set it to `0` to stop the wave from moving.
153
+ */
154
+ waveSpeed?: number;
155
+ };
156
+
157
+ // endregion
158
+
131
159
  // region LinearWavyProgressIndicator
132
160
 
133
- export interface LinearWavyProgressIndicatorProps extends ProgressCommonConfig {
161
+ export interface LinearWavyProgressIndicatorProps
162
+ extends ProgressCommonConfig, WavyProgressCommonConfig {
134
163
  /**
135
164
  * Size of the stop indicator in dp at the end of the determinate progress track.
136
165
  */
@@ -149,7 +178,8 @@ export const LinearWavyProgressIndicator =
149
178
 
150
179
  // region CircularWavyProgressIndicator
151
180
 
152
- export interface CircularWavyProgressIndicatorProps extends ProgressCommonConfig {}
181
+ export interface CircularWavyProgressIndicatorProps
182
+ extends ProgressCommonConfig, WavyProgressCommonConfig {}
153
183
 
154
184
  /**
155
185
  * A circular progress indicator with wavy animation style.
@@ -1,7 +1,7 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import type { ReactElement, ComponentType } from 'react';
3
- import type { StyleProp, ViewStyle } from 'react-native';
4
3
 
4
+ import { PresentedContentContext, useIsPresentedInOwnWindow } from '../../PresentedContentContext';
5
5
  import type { ModifierConfig } from '../../types';
6
6
  import type { PrimitiveBaseProps } from '../layout';
7
7
  import { createViewModifierEventListener } from '../modifiers/utils';
@@ -22,11 +22,6 @@ export interface RNHostProps extends PrimitiveBaseProps {
22
22
  * Modifiers for the component.
23
23
  */
24
24
  modifiers?: ModifierConfig[];
25
- /**
26
- * Style applied to the host view's React Native shadow node. Useful for
27
- * controlling its layout position (e.g. `position: 'absolute'`).
28
- */
29
- style?: StyleProp<ViewStyle>;
30
25
  }
31
26
 
32
27
  type NativeRNHostProps = RNHostProps & {
@@ -37,25 +32,33 @@ const NativeRNHostView: ComponentType<NativeRNHostProps> = requireNativeView(
37
32
  'RNHostView'
38
33
  );
39
34
 
40
- function transformProps(props: RNHostProps): NativeRNHostProps {
35
+ function transformProps(props: RNHostProps, layoutRoot: boolean): NativeRNHostProps {
41
36
  const { modifiers, ...restProps } = props;
42
37
  return {
43
38
  modifiers,
44
39
  ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
45
40
  ...restProps,
46
- // Touches on hosted content are dispatched relative to the host, so `measure()` must report
47
- // the same coordinate space; otherwise `Pressable` cancels the press on any finger movement.
48
- layoutRoot: true,
41
+ layoutRoot,
49
42
  };
50
43
  }
51
44
 
52
45
  export function RNHostView(props: RNHostProps) {
46
+ // Content presented in its own window — a modal bottom sheet, a dialog — has no React root above
47
+ // it, so it dispatches its own touches and is measured from itself. Everywhere else the surface
48
+ // root already streams this subtree's touches, and a second stream in a second coordinate space
49
+ // is what makes a `Pressable` drop its press on the first finger movement.
50
+ const layoutRoot = useIsPresentedInOwnWindow();
51
+
53
52
  return (
54
53
  <NativeRNHostView
55
- {...transformProps(props)}
54
+ {...transformProps(props, layoutRoot)}
56
55
  // `matchContents` can only be used once on mount
57
56
  // So we force unmount when it changes to prevent unexpected layout
58
- key={props.matchContents ? 'matchContents' : 'noMatchContents'}
59
- />
57
+ key={props.matchContents ? 'matchContents' : 'noMatchContents'}>
58
+ {/* Reset context here so only nearest RNHostView becomes the layout root for its children, and not any other RNHostView above it in the tree. */}
59
+ <PresentedContentContext.Provider value={false}>
60
+ {props.children}
61
+ </PresentedContentContext.Provider>
62
+ </NativeRNHostView>
60
63
  );
61
64
  }
@@ -2,6 +2,7 @@ import type { ReactNode, Ref } from 'react';
2
2
  import type { ColorValue } from 'react-native';
3
3
 
4
4
  import { getStateId, type ObservableState, useWorkletProp, worklets } from '../../State';
5
+ import { useHostedTextInput } from '../../keyboard';
5
6
  import type { ModifierConfig, ViewEvent } from '../../types';
6
7
  import { createViewModifierEventListener } from '../modifiers/utils';
7
8
 
@@ -180,6 +181,7 @@ export type CommonTextFieldProperties = {
180
181
  * else on the props passes through untouched.
181
182
  */
182
183
  type TransformedKeys =
184
+ | 'ref'
183
185
  | 'value'
184
186
  | 'selection'
185
187
  | 'modifiers'
@@ -195,6 +197,7 @@ type TransformedKeys =
195
197
  * callbacks become `nativeEvent`-wrapped listeners.
196
198
  */
197
199
  export type CommonNativeTextFieldProps = {
200
+ ref?: Ref<any>;
198
201
  modifiers?: ModifierConfig[];
199
202
  children?: ReactNode;
200
203
  value?: number | null;
@@ -209,6 +212,7 @@ export function useCommonTextFieldProps<T extends CommonTextFieldProperties>(
209
212
  props: T
210
213
  ): CommonNativeTextFieldProps & Omit<T, TransformedKeys> {
211
214
  const {
215
+ ref,
212
216
  value,
213
217
  selection,
214
218
  modifiers,
@@ -222,9 +226,11 @@ export function useCommonTextFieldProps<T extends CommonTextFieldProperties>(
222
226
 
223
227
  const isWorklet = !!onValueChange && !!worklets?.isWorkletFunction?.(onValueChange);
224
228
  const workletCallback = useWorkletProp(isWorklet ? onValueChange : undefined, 'onValueChange');
229
+ const hosted = useHostedTextInput<TextFieldRef>(ref, onFocusChanged);
225
230
 
226
231
  return {
227
232
  ...rest,
233
+ ref: hosted.ref,
228
234
  modifiers,
229
235
  ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
230
236
  children,
@@ -233,7 +239,7 @@ export function useCommonTextFieldProps<T extends CommonTextFieldProperties>(
233
239
  onValueChangeSync: getStateId(workletCallback),
234
240
  onValueChange:
235
241
  !isWorklet && onValueChange ? (event) => onValueChange(event.nativeEvent.text) : undefined,
236
- onFocusChanged: onFocusChanged ? (event) => onFocusChanged(event.nativeEvent.value) : undefined,
242
+ onFocusChanged: (event) => hosted.onFocusChange(event.nativeEvent.value),
237
243
  onSelectionChange: onSelectionChange
238
244
  ? (event) => onSelectionChange({ start: event.nativeEvent.start, end: event.nativeEvent.end })
239
245
  : undefined,
@@ -2,6 +2,7 @@ import { requireNativeView } from 'expo';
2
2
  import { type Ref } from 'react';
3
3
  import { type ColorValue } from 'react-native';
4
4
 
5
+ import { PresentedContent } from '../../PresentedContentContext';
5
6
  import { type ModifierConfig } from '../../types';
6
7
  import { createViewModifierEventListener } from '../modifiers/utils';
7
8
 
@@ -146,7 +147,7 @@ function RichTooltipComponent(props: RichTooltipProps) {
146
147
  modifiers={modifiers}
147
148
  {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
148
149
  {...restProps}>
149
- {children}
150
+ <PresentedContent>{children}</PresentedContent>
150
151
  </RichTooltipNativeView>
151
152
  </SlotNativeView>
152
153
  );