@expo/ui 56.0.8 → 56.0.9
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.
- package/CHANGELOG.md +22 -1
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +38 -1
- package/android/src/main/java/expo/modules/ui/LoadingView.kt +80 -0
- package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +31 -3
- package/android/src/main/java/expo/modules/ui/SnackbarView.kt +126 -0
- package/android/src/main/java/expo/modules/ui/state/ObservableState.kt +10 -0
- package/assets/keyboard_arrow_down.xml +10 -0
- package/build/State/useNativeState.d.ts +32 -3
- package/build/State/useNativeState.d.ts.map +1 -1
- package/build/community/bottom-sheet/BottomSheet.ios.d.ts.map +1 -1
- package/build/jetpack-compose/LoadingIndicator/index.d.ts +41 -0
- package/build/jetpack-compose/LoadingIndicator/index.d.ts.map +1 -0
- package/build/jetpack-compose/Snackbar/index.d.ts +94 -0
- package/build/jetpack-compose/Snackbar/index.d.ts.map +1 -0
- package/build/jetpack-compose/index.d.ts +2 -0
- package/build/jetpack-compose/index.d.ts.map +1 -1
- package/build/jetpack-compose/modifiers/index.d.ts +6 -2
- package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/BottomSheet/index.d.ts +5 -1
- package/build/swift-ui/BottomSheet/index.d.ts.map +1 -1
- package/build/swift-ui/index.d.ts +1 -0
- package/build/swift-ui/index.d.ts.map +1 -1
- package/build/swift-ui/withAnimation.d.ts +26 -0
- package/build/swift-ui/withAnimation.d.ts.map +1 -0
- package/build/universal/BottomSheet/index.android.d.ts +1 -1
- package/build/universal/BottomSheet/index.android.d.ts.map +1 -1
- package/build/universal/BottomSheet/index.d.ts +1 -1
- package/build/universal/BottomSheet/index.d.ts.map +1 -1
- package/build/universal/BottomSheet/index.ios.d.ts +1 -1
- package/build/universal/BottomSheet/index.ios.d.ts.map +1 -1
- package/build/universal/BottomSheet/types.d.ts +27 -0
- package/build/universal/BottomSheet/types.d.ts.map +1 -1
- package/build/universal/Collapsible/index.android.d.ts +8 -0
- package/build/universal/Collapsible/index.android.d.ts.map +1 -0
- package/build/universal/Collapsible/index.d.ts +8 -0
- package/build/universal/Collapsible/index.d.ts.map +1 -0
- package/build/universal/Collapsible/index.ios.d.ts +7 -0
- package/build/universal/Collapsible/index.ios.d.ts.map +1 -0
- package/build/universal/Collapsible/types.d.ts +23 -0
- package/build/universal/Collapsible/types.d.ts.map +1 -0
- package/build/universal/Column/index.d.ts.map +1 -1
- package/build/universal/Host/index.d.ts +5 -18
- package/build/universal/Host/index.d.ts.map +1 -1
- package/build/universal/Host/types.d.ts +72 -0
- package/build/universal/Host/types.d.ts.map +1 -0
- package/build/universal/List/index.android.d.ts +9 -0
- package/build/universal/List/index.android.d.ts.map +1 -0
- package/build/universal/List/index.d.ts +8 -0
- package/build/universal/List/index.d.ts.map +1 -0
- package/build/universal/List/index.ios.d.ts +8 -0
- package/build/universal/List/index.ios.d.ts.map +1 -0
- package/build/universal/List/types.d.ts +26 -0
- package/build/universal/List/types.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.android.d.ts +8 -0
- package/build/universal/ListItem/ListItem.android.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.d.ts +9 -0
- package/build/universal/ListItem/ListItem.d.ts.map +1 -0
- package/build/universal/ListItem/ListItem.ios.d.ts +8 -0
- package/build/universal/ListItem/ListItem.ios.d.ts.map +1 -0
- package/build/universal/ListItem/ListItemSlots.d.ts +21 -0
- package/build/universal/ListItem/ListItemSlots.d.ts.map +1 -0
- package/build/universal/ListItem/index.d.ts +10 -0
- package/build/universal/ListItem/index.d.ts.map +1 -0
- package/build/universal/ListItem/types.d.ts +59 -0
- package/build/universal/ListItem/types.d.ts.map +1 -0
- package/build/universal/Picker/Picker.android.d.ts +9 -0
- package/build/universal/Picker/Picker.android.d.ts.map +1 -0
- package/build/universal/Picker/Picker.d.ts +8 -0
- package/build/universal/Picker/Picker.d.ts.map +1 -0
- package/build/universal/Picker/Picker.ios.d.ts +9 -0
- package/build/universal/Picker/Picker.ios.d.ts.map +1 -0
- package/build/universal/Picker/PickerItem.d.ts +9 -0
- package/build/universal/Picker/PickerItem.d.ts.map +1 -0
- package/build/universal/Picker/index.d.ts +8 -0
- package/build/universal/Picker/index.d.ts.map +1 -0
- package/build/universal/Picker/types.d.ts +69 -0
- package/build/universal/Picker/types.d.ts.map +1 -0
- package/build/universal/index.d.ts +4 -0
- package/build/universal/index.d.ts.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/BottomSheetView.swift +4 -1
- package/ios/ExpoUIModule.swift +41 -1
- package/ios/Modifiers/AnimationConfig.swift +109 -0
- package/ios/Modifiers/ViewModifierRegistry.swift +1 -112
- package/ios/State/ObservableState.swift +12 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8-sources.jar → 56.0.9/expo.modules.ui-56.0.9-sources.jar} +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8.module → 56.0.9/expo.modules.ui-56.0.9.module} +22 -22
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.8/expo.modules.ui-56.0.8.pom → 56.0.9/expo.modules.ui-56.0.9.pom} +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.9/expo.modules.ui-56.0.9.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
- package/package.json +3 -3
- package/src/State/useNativeState.ts +70 -10
- package/src/community/bottom-sheet/BottomSheet.ios.tsx +0 -17
- package/src/jetpack-compose/LoadingIndicator/index.tsx +92 -0
- package/src/jetpack-compose/Snackbar/index.tsx +135 -0
- package/src/jetpack-compose/index.ts +2 -0
- package/src/jetpack-compose/modifiers/index.ts +5 -2
- package/src/swift-ui/BottomSheet/index.tsx +32 -15
- package/src/swift-ui/index.tsx +1 -0
- package/src/swift-ui/withAnimation.ts +71 -0
- package/src/ts-declarations/react-native-web.d.ts +7 -0
- package/src/universal/BottomSheet/index.android.tsx +27 -3
- package/src/universal/BottomSheet/index.ios.tsx +30 -12
- package/src/universal/BottomSheet/index.tsx +46 -4
- package/src/universal/BottomSheet/types.ts +25 -0
- package/src/universal/Collapsible/index.android.tsx +72 -0
- package/src/universal/Collapsible/index.ios.tsx +16 -0
- package/src/universal/Collapsible/index.tsx +58 -0
- package/src/universal/Collapsible/types.ts +25 -0
- package/src/universal/Column/index.tsx +3 -1
- package/src/universal/Host/index.tsx +9 -10
- package/src/universal/Host/types.ts +70 -0
- package/src/universal/List/index.android.tsx +44 -0
- package/src/universal/List/index.ios.tsx +19 -0
- package/src/universal/List/index.tsx +26 -0
- package/src/universal/List/types.ts +28 -0
- package/src/universal/ListItem/ListItem.android.tsx +52 -0
- package/src/universal/ListItem/ListItem.ios.tsx +58 -0
- package/src/universal/ListItem/ListItem.tsx +72 -0
- package/src/universal/ListItem/ListItemSlots.tsx +66 -0
- package/src/universal/ListItem/index.ts +15 -0
- package/src/universal/ListItem/types.ts +67 -0
- package/src/universal/Picker/Picker.android.tsx +69 -0
- package/src/universal/Picker/Picker.ios.tsx +45 -0
- package/src/universal/Picker/Picker.tsx +52 -0
- package/src/universal/Picker/PickerItem.tsx +27 -0
- package/src/universal/Picker/index.ts +11 -0
- package/src/universal/Picker/types.ts +79 -0
- package/src/universal/index.ts +4 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.8/expo.modules.ui-56.0.8.pom.sha512 +0 -1
- package/src/community/bottom-sheet/CLAUDE.md +0 -55
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { requireNativeView } from 'expo';
|
|
2
|
-
import type
|
|
2
|
+
import { useState, type ComponentType } from 'react';
|
|
3
3
|
import type { NativeSyntheticEvent } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { createViewModifierEventListener } from '../modifiers/utils';
|
|
@@ -21,6 +21,10 @@ export type BottomSheetProps = {
|
|
|
21
21
|
* Callback function that is called when the `BottomSheet` presented state changes.
|
|
22
22
|
*/
|
|
23
23
|
onIsPresentedChange: (isPresented: boolean) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Callback function that is called after the `BottomSheet` has been fully dismissed.
|
|
26
|
+
*/
|
|
27
|
+
onDismiss?: () => void;
|
|
24
28
|
/**
|
|
25
29
|
* When `true`, the sheet will automatically size itself to fit its content.
|
|
26
30
|
* This sets the presentation detent to match the height of the children.
|
|
@@ -29,8 +33,9 @@ export type BottomSheetProps = {
|
|
|
29
33
|
fitToContents?: boolean;
|
|
30
34
|
} & CommonViewModifierProps;
|
|
31
35
|
|
|
32
|
-
type NativeBottomSheetProps = Omit<BottomSheetProps, 'onIsPresentedChange'> & {
|
|
36
|
+
type NativeBottomSheetProps = Omit<BottomSheetProps, 'onIsPresentedChange' | 'onDismiss'> & {
|
|
33
37
|
onIsPresentedChange: (event: NativeSyntheticEvent<{ isPresented: boolean }>) => void;
|
|
38
|
+
onDismiss: (event: NativeSyntheticEvent<object>) => void;
|
|
34
39
|
};
|
|
35
40
|
|
|
36
41
|
const BottomSheetNativeView: ComponentType<NativeBottomSheetProps> = requireNativeView(
|
|
@@ -38,23 +43,35 @@ const BottomSheetNativeView: ComponentType<NativeBottomSheetProps> = requireNati
|
|
|
38
43
|
'BottomSheetView'
|
|
39
44
|
);
|
|
40
45
|
|
|
41
|
-
function transformBottomSheetProps(props: BottomSheetProps): NativeBottomSheetProps {
|
|
42
|
-
const { modifiers, ...restProps } = props;
|
|
43
|
-
return {
|
|
44
|
-
modifiers,
|
|
45
|
-
...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
|
|
46
|
-
...restProps,
|
|
47
|
-
onIsPresentedChange: ({ nativeEvent: { isPresented } }) => {
|
|
48
|
-
props?.onIsPresentedChange?.(isPresented);
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
46
|
/**
|
|
54
47
|
* `BottomSheet` presents content from the bottom of the screen.
|
|
55
48
|
*/
|
|
56
49
|
function BottomSheet(props: BottomSheetProps) {
|
|
57
|
-
|
|
50
|
+
const { modifiers, onIsPresentedChange, onDismiss, ...restProps } = props;
|
|
51
|
+
const [isMounted, setIsMounted] = useState(props.isPresented);
|
|
52
|
+
|
|
53
|
+
if (props.isPresented && !isMounted) {
|
|
54
|
+
setIsMounted(true);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!isMounted) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<BottomSheetNativeView
|
|
63
|
+
modifiers={modifiers}
|
|
64
|
+
{...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
|
|
65
|
+
{...restProps}
|
|
66
|
+
onIsPresentedChange={({ nativeEvent: { isPresented } }) => {
|
|
67
|
+
onIsPresentedChange?.(isPresented);
|
|
68
|
+
}}
|
|
69
|
+
onDismiss={() => {
|
|
70
|
+
setIsMounted(false);
|
|
71
|
+
onDismiss?.();
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
export { BottomSheet };
|
package/src/swift-ui/index.tsx
CHANGED
|
@@ -37,6 +37,7 @@ export * from './Stepper';
|
|
|
37
37
|
export * from './SwipeActions';
|
|
38
38
|
export * from './Text';
|
|
39
39
|
export { useNativeState } from '../State/useNativeState';
|
|
40
|
+
export { withAnimation, type WithAnimationCompletionCriteria } from './withAnimation';
|
|
40
41
|
export * from './SyncToggle';
|
|
41
42
|
export * from './TabView';
|
|
42
43
|
export * from './Toggle';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { requireNativeModule } from 'expo';
|
|
2
|
+
|
|
3
|
+
import { worklets } from '../State/optionalWorklets';
|
|
4
|
+
import { VALUE_SYMBOL } from './modifiers/animation/constants';
|
|
5
|
+
import type { AnimationObject, ChainableAnimationType } from './modifiers/animation/types';
|
|
6
|
+
|
|
7
|
+
const ExpoUI = requireNativeModule('ExpoUI');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/animationcompletioncriteria).
|
|
11
|
+
*/
|
|
12
|
+
export type WithAnimationCompletionCriteria = 'logicallyComplete' | 'removed';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Mirrors SwiftUI's [`withAnimation(_:_:)`](https://developer.apple.com/documentation/swiftui/withanimation(_:_:)).
|
|
16
|
+
* The body must be a worklet so the mutations run synchronously on the
|
|
17
|
+
* UI thread inside the animation transaction.
|
|
18
|
+
*
|
|
19
|
+
* Performs `body` inside a SwiftUI animation transaction. Any
|
|
20
|
+
* `useNativeState` values mutated by the worklet animate to their new value
|
|
21
|
+
* using `animation`.
|
|
22
|
+
*
|
|
23
|
+
* @param animation Animation to apply, built with the `Animation` factory
|
|
24
|
+
* from `@expo/ui/swift-ui/modifiers`. Pass `null` to run `body` without an
|
|
25
|
+
* animation.
|
|
26
|
+
* @param body Worklet that mutates one or more `useNativeState` values.
|
|
27
|
+
* @param completion Optional worklet invoked on the main thread when the
|
|
28
|
+
* animation finishes. Requires iOS 17 / tvOS 17; on earlier versions the
|
|
29
|
+
* animation still runs but the callback is silently skipped.
|
|
30
|
+
* @param completionCriteria Controls when `completion` fires. Defaults to
|
|
31
|
+
* `'logicallyComplete'`.
|
|
32
|
+
*/
|
|
33
|
+
export function withAnimation(
|
|
34
|
+
animation: ChainableAnimationType | null,
|
|
35
|
+
body: () => void,
|
|
36
|
+
completion?: () => void,
|
|
37
|
+
completionCriteria?: WithAnimationCompletionCriteria
|
|
38
|
+
): void {
|
|
39
|
+
if (!worklets) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"withAnimation needs the 'react-native-worklets' package, which couldn't be loaded. " +
|
|
42
|
+
'Install react-native-worklets and rebuild the native app, then call withAnimation again.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!worklets.isWorkletFunction(body)) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
'withAnimation body must be a worklet. Worklets run synchronously on the UI thread ' +
|
|
49
|
+
"so state changes are captured by SwiftUI's animation transaction. " +
|
|
50
|
+
"Add the 'worklet' directive as the first statement: " +
|
|
51
|
+
"() => { 'worklet'; state.value = next; }."
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let completionCallback: object | null = null;
|
|
56
|
+
if (completion) {
|
|
57
|
+
if (!worklets.isWorkletFunction(completion)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"withAnimation completion must be a worklet. Add the 'worklet' directive as the " +
|
|
60
|
+
'first statement in your completion callback.'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
completionCallback = new ExpoUI.WorkletCallback(worklets.createSerializable(completion));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const animationObject: AnimationObject | null =
|
|
67
|
+
animation == null ? null : animation[VALUE_SYMBOL]();
|
|
68
|
+
|
|
69
|
+
const bodyCallback = new ExpoUI.WorkletCallback(worklets.createSerializable(body));
|
|
70
|
+
ExpoUI.withAnimation(animationObject, bodyCallback, completionCallback, completionCriteria);
|
|
71
|
+
}
|
|
@@ -7,6 +7,7 @@ declare module 'react-native' {
|
|
|
7
7
|
) => React.ReactElement<P>;
|
|
8
8
|
|
|
9
9
|
type DisplayValue = ReactNative.FlexStyle['display'] | 'inline-flex';
|
|
10
|
+
type OverflowValue = 'auto' | 'hidden' | 'scroll' | 'visible';
|
|
10
11
|
type WebDimensionValue = ReactNative.DimensionValue | string;
|
|
11
12
|
|
|
12
13
|
type WebRole =
|
|
@@ -104,6 +105,8 @@ declare module 'react-native' {
|
|
|
104
105
|
display?: DisplayValue;
|
|
105
106
|
height?: WebDimensionValue;
|
|
106
107
|
width?: WebDimensionValue;
|
|
108
|
+
overflowX?: OverflowValue;
|
|
109
|
+
overflowY?: OverflowValue;
|
|
107
110
|
paddingLeft?: WebDimensionValue;
|
|
108
111
|
paddingRight?: WebDimensionValue;
|
|
109
112
|
paddingTop?: WebDimensionValue;
|
|
@@ -114,6 +117,8 @@ declare module 'react-native' {
|
|
|
114
117
|
display?: DisplayValue;
|
|
115
118
|
height?: WebDimensionValue;
|
|
116
119
|
width?: WebDimensionValue;
|
|
120
|
+
overflowX?: OverflowValue;
|
|
121
|
+
overflowY?: OverflowValue;
|
|
117
122
|
paddingLeft?: WebDimensionValue;
|
|
118
123
|
paddingRight?: WebDimensionValue;
|
|
119
124
|
paddingTop?: WebDimensionValue;
|
|
@@ -124,6 +129,8 @@ declare module 'react-native' {
|
|
|
124
129
|
display?: DisplayValue;
|
|
125
130
|
height?: WebDimensionValue;
|
|
126
131
|
width?: WebDimensionValue;
|
|
132
|
+
overflowX?: OverflowValue;
|
|
133
|
+
overflowY?: OverflowValue;
|
|
127
134
|
paddingLeft?: WebDimensionValue;
|
|
128
135
|
paddingRight?: WebDimensionValue;
|
|
129
136
|
paddingTop?: WebDimensionValue;
|
|
@@ -1,32 +1,56 @@
|
|
|
1
1
|
import { Column, ModalBottomSheet } from '@expo/ui/jetpack-compose';
|
|
2
2
|
import {
|
|
3
|
+
fillMaxHeight,
|
|
3
4
|
padding,
|
|
4
5
|
testID as testIDModifier,
|
|
5
6
|
type ModifierConfig,
|
|
6
7
|
} from '@expo/ui/jetpack-compose/modifiers';
|
|
7
8
|
|
|
8
|
-
import type { BottomSheetProps } from './types';
|
|
9
|
+
import type { BottomSheetProps, SnapPoint } from './types';
|
|
10
|
+
|
|
11
|
+
// M3 `ModalBottomSheet` only has partial/expanded states.
|
|
12
|
+
// Only allow the partial state when the consumer requested a partial-friendly snap point.
|
|
13
|
+
function shouldSkipPartiallyExpanded(snapPoints: SnapPoint[] | undefined): boolean {
|
|
14
|
+
if (!snapPoints || snapPoints.length === 0) return false;
|
|
15
|
+
return !snapPoints.some(
|
|
16
|
+
(sp) =>
|
|
17
|
+
sp === 'half' ||
|
|
18
|
+
(typeof sp === 'object' && 'fraction' in sp && sp.fraction < 1) ||
|
|
19
|
+
(typeof sp === 'object' && 'height' in sp)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// M3 sizes content to intrinsic height.
|
|
24
|
+
// Apply `fillMaxHeight` so `'full'` actually fills the viewport instead of stopping at content height.
|
|
25
|
+
function shouldFillMaxHeight(snapPoints: SnapPoint[] | undefined): boolean {
|
|
26
|
+
if (!snapPoints || snapPoints.length === 0) return false;
|
|
27
|
+
return snapPoints.some(
|
|
28
|
+
(sp) => sp === 'full' || (typeof sp === 'object' && 'fraction' in sp && sp.fraction >= 1)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
9
31
|
|
|
10
32
|
export function BottomSheet({
|
|
11
33
|
children,
|
|
12
34
|
isPresented,
|
|
13
35
|
onDismiss,
|
|
14
36
|
showDragIndicator = true,
|
|
37
|
+
snapPoints,
|
|
15
38
|
testID,
|
|
16
39
|
modifiers,
|
|
17
40
|
}: BottomSheetProps) {
|
|
18
41
|
if (!isPresented) return null;
|
|
19
42
|
|
|
20
43
|
const contentModifiers: ModifierConfig[] = [padding(16, showDragIndicator ? 0 : 16, 16, 0)];
|
|
44
|
+
if (shouldFillMaxHeight(snapPoints)) contentModifiers.push(fillMaxHeight());
|
|
21
45
|
if (testID) contentModifiers.push(testIDModifier(testID));
|
|
22
46
|
|
|
23
47
|
return (
|
|
24
48
|
<ModalBottomSheet
|
|
25
49
|
onDismissRequest={onDismiss}
|
|
26
50
|
showDragHandle={showDragIndicator}
|
|
51
|
+
skipPartiallyExpanded={shouldSkipPartiallyExpanded(snapPoints)}
|
|
27
52
|
modifiers={modifiers}>
|
|
28
|
-
{/* When the drag handle is hidden, add top padding so content doesn't
|
|
29
|
-
crop against the top edge of the sheet. */}
|
|
53
|
+
{/* When the drag handle is hidden, add top padding so content doesn't crop against the top edge of the sheet. */}
|
|
30
54
|
<Column modifiers={contentModifiers}>{children}</Column>
|
|
31
55
|
</ModalBottomSheet>
|
|
32
56
|
);
|
|
@@ -1,33 +1,51 @@
|
|
|
1
1
|
import { BottomSheet as SwiftUIBottomSheet, Group } from '@expo/ui/swift-ui';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
frame,
|
|
4
|
+
padding,
|
|
5
|
+
presentationDetents,
|
|
6
|
+
presentationDragIndicator,
|
|
7
|
+
type ModifierConfig,
|
|
8
|
+
type PresentationDetent,
|
|
9
|
+
} from '@expo/ui/swift-ui/modifiers';
|
|
3
10
|
|
|
4
|
-
import type { BottomSheetProps } from './types';
|
|
11
|
+
import type { BottomSheetProps, SnapPoint } from './types';
|
|
12
|
+
|
|
13
|
+
function snapPointToDetent(snapPoint: SnapPoint): PresentationDetent {
|
|
14
|
+
if (snapPoint === 'half') return 'medium';
|
|
15
|
+
if (snapPoint === 'full') return 'large';
|
|
16
|
+
return snapPoint;
|
|
17
|
+
}
|
|
5
18
|
|
|
6
19
|
export function BottomSheet({
|
|
7
20
|
children,
|
|
8
21
|
isPresented,
|
|
9
22
|
onDismiss,
|
|
10
23
|
showDragIndicator = true,
|
|
24
|
+
snapPoints,
|
|
11
25
|
testID,
|
|
12
26
|
modifiers,
|
|
13
27
|
}: BottomSheetProps) {
|
|
28
|
+
const presentationModifiers: ModifierConfig[] = [
|
|
29
|
+
frame({ maxWidth: Infinity, alignment: 'topLeading' }),
|
|
30
|
+
padding({ top: 16, leading: 16, trailing: 16 }),
|
|
31
|
+
presentationDragIndicator(showDragIndicator ? 'visible' : 'hidden'),
|
|
32
|
+
];
|
|
33
|
+
if (snapPoints && snapPoints.length > 0) {
|
|
34
|
+
presentationModifiers.push(presentationDetents(snapPoints.map(snapPointToDetent)));
|
|
35
|
+
}
|
|
36
|
+
if (modifiers?.length) {
|
|
37
|
+
presentationModifiers.push(...modifiers);
|
|
38
|
+
}
|
|
39
|
+
|
|
14
40
|
return (
|
|
15
41
|
<SwiftUIBottomSheet
|
|
16
42
|
isPresented={isPresented}
|
|
17
43
|
onIsPresentedChange={(presented) => {
|
|
18
44
|
if (!presented) onDismiss();
|
|
19
45
|
}}
|
|
20
|
-
fitToContents
|
|
46
|
+
fitToContents={!snapPoints || snapPoints.length === 0}
|
|
21
47
|
testID={testID}>
|
|
22
|
-
<Group
|
|
23
|
-
modifiers={[
|
|
24
|
-
frame({ maxWidth: Infinity, alignment: 'topLeading' }),
|
|
25
|
-
padding({ top: 16, leading: 16, trailing: 16 }),
|
|
26
|
-
presentationDragIndicator(showDragIndicator ? 'visible' : 'hidden'),
|
|
27
|
-
...(modifiers ?? []),
|
|
28
|
-
]}>
|
|
29
|
-
{children}
|
|
30
|
-
</Group>
|
|
48
|
+
<Group modifiers={presentationModifiers}>{children}</Group>
|
|
31
49
|
</SwiftUIBottomSheet>
|
|
32
50
|
);
|
|
33
51
|
}
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { Drawer } from 'vaul';
|
|
2
2
|
|
|
3
|
-
import type { BottomSheetProps } from './types';
|
|
3
|
+
import type { BottomSheetProps, SnapPoint } from './types';
|
|
4
|
+
|
|
5
|
+
// Visually-hidden style for the screen-reader-only Drawer.Title.
|
|
6
|
+
const visuallyHiddenStyle: React.CSSProperties = {
|
|
7
|
+
position: 'absolute',
|
|
8
|
+
width: 1,
|
|
9
|
+
height: 1,
|
|
10
|
+
padding: 0,
|
|
11
|
+
margin: -1,
|
|
12
|
+
overflow: 'hidden',
|
|
13
|
+
clip: 'rect(0, 0, 0, 0)',
|
|
14
|
+
whiteSpace: 'nowrap',
|
|
15
|
+
border: 0,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function snapPointToVaul(snapPoint: SnapPoint): string | number {
|
|
19
|
+
if (snapPoint === 'half') return 0.5;
|
|
20
|
+
if (snapPoint === 'full') return 1;
|
|
21
|
+
if ('fraction' in snapPoint) return snapPoint.fraction;
|
|
22
|
+
return `${snapPoint.height}px`;
|
|
23
|
+
}
|
|
4
24
|
|
|
5
25
|
/**
|
|
6
26
|
* A modal sheet that slides up from the bottom of the screen.
|
|
@@ -10,17 +30,32 @@ export function BottomSheet({
|
|
|
10
30
|
isPresented,
|
|
11
31
|
onDismiss,
|
|
12
32
|
showDragIndicator = true,
|
|
33
|
+
snapPoints,
|
|
13
34
|
testID,
|
|
14
35
|
}: BottomSheetProps) {
|
|
36
|
+
const vaulSnapPoints = snapPoints?.length ? snapPoints.map(snapPointToVaul) : undefined;
|
|
37
|
+
const hasSnapPoints = vaulSnapPoints != null;
|
|
38
|
+
|
|
15
39
|
return (
|
|
16
40
|
<Drawer.Root
|
|
17
41
|
open={isPresented}
|
|
18
42
|
onOpenChange={(open) => {
|
|
19
43
|
if (!open) onDismiss();
|
|
20
|
-
}}
|
|
44
|
+
}}
|
|
45
|
+
snapPoints={vaulSnapPoints}>
|
|
21
46
|
<Drawer.Portal>
|
|
22
47
|
<Drawer.Overlay style={overlayStyle} />
|
|
23
|
-
<Drawer.Content
|
|
48
|
+
<Drawer.Content
|
|
49
|
+
style={{
|
|
50
|
+
...contentStyle,
|
|
51
|
+
// Snap-points mode: vaul translates the drawer by `viewport - snapHeight`.
|
|
52
|
+
// The drawer has to fill the viewport or it gets pushed off-screen.
|
|
53
|
+
...(hasSnapPoints ? snapPointContentStyle : noSnapPointContentStyle),
|
|
54
|
+
...(showDragIndicator && dragIndicatorSpacing),
|
|
55
|
+
}}
|
|
56
|
+
aria-describedby={undefined}>
|
|
57
|
+
{/* Radix Dialog requires a title for a11y; render visually-hidden. */}
|
|
58
|
+
<Drawer.Title style={visuallyHiddenStyle}>Bottom sheet</Drawer.Title>
|
|
24
59
|
{showDragIndicator && <Drawer.Handle />}
|
|
25
60
|
<div style={innerStyle} data-testid={testID}>
|
|
26
61
|
{children}
|
|
@@ -47,12 +82,19 @@ const contentStyle: React.CSSProperties = {
|
|
|
47
82
|
borderTopLeftRadius: 16,
|
|
48
83
|
borderTopRightRadius: 16,
|
|
49
84
|
zIndex: 50,
|
|
50
|
-
maxHeight: '85vh',
|
|
51
85
|
outline: 'none',
|
|
52
86
|
display: 'flex',
|
|
53
87
|
flexDirection: 'column',
|
|
54
88
|
};
|
|
55
89
|
|
|
90
|
+
const snapPointContentStyle: React.CSSProperties = {
|
|
91
|
+
height: '96vh',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const noSnapPointContentStyle: React.CSSProperties = {
|
|
95
|
+
maxHeight: '85vh',
|
|
96
|
+
};
|
|
97
|
+
|
|
56
98
|
const innerStyle: React.CSSProperties = {
|
|
57
99
|
padding: 16,
|
|
58
100
|
overflow: 'auto',
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { ModifierConfig } from '../../types';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A snap point describing one of the heights a [`BottomSheet`](#bottomsheet) can rest at.
|
|
5
|
+
*
|
|
6
|
+
* - `'half'` — Approximately half-screen.
|
|
7
|
+
* - `'full'` — Fully expanded.
|
|
8
|
+
* - `{ fraction }` — A fraction of the screen height (0–1).
|
|
9
|
+
* iOS / web only.
|
|
10
|
+
* - `{ height }` — A fixed pixel height.
|
|
11
|
+
* iOS / web only.
|
|
12
|
+
*
|
|
13
|
+
* On Android, `{ fraction }` and `{ height }` snap to the nearest of `'half'` / `'full'`.
|
|
14
|
+
* See the component docs for platform behavior notes.
|
|
15
|
+
*/
|
|
16
|
+
export type SnapPoint = 'half' | 'full' | { fraction: number } | { height: number };
|
|
17
|
+
|
|
3
18
|
/**
|
|
4
19
|
* Props for the [`BottomSheet`](#bottomsheet) component, a modal sheet that slides up from the bottom of the screen.
|
|
5
20
|
*/
|
|
@@ -25,6 +40,16 @@ export interface BottomSheetProps {
|
|
|
25
40
|
*/
|
|
26
41
|
showDragIndicator?: boolean;
|
|
27
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Heights the sheet can rest at.
|
|
45
|
+
* When omitted, the sheet auto-sizes to its content.
|
|
46
|
+
* See [`SnapPoint`](#snappoint) for the supported values.
|
|
47
|
+
*
|
|
48
|
+
* @example `['half', 'full']` — draggable between half and full
|
|
49
|
+
* @example `['full']` — always full height
|
|
50
|
+
*/
|
|
51
|
+
snapPoints?: SnapPoint[];
|
|
52
|
+
|
|
28
53
|
/**
|
|
29
54
|
* Identifier used to locate the component in end-to-end tests.
|
|
30
55
|
*/
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnimatedVisibility,
|
|
3
|
+
Column,
|
|
4
|
+
EnterTransition,
|
|
5
|
+
ExitTransition,
|
|
6
|
+
Icon,
|
|
7
|
+
ListItem,
|
|
8
|
+
Text,
|
|
9
|
+
useMaterialColors,
|
|
10
|
+
} from '@expo/ui/jetpack-compose';
|
|
11
|
+
import {
|
|
12
|
+
animated,
|
|
13
|
+
background,
|
|
14
|
+
clickable,
|
|
15
|
+
clip,
|
|
16
|
+
graphicsLayer,
|
|
17
|
+
padding,
|
|
18
|
+
Shapes,
|
|
19
|
+
spring,
|
|
20
|
+
} from '@expo/ui/jetpack-compose/modifiers';
|
|
21
|
+
|
|
22
|
+
import type { CollapsibleProps } from './types';
|
|
23
|
+
|
|
24
|
+
const KEYBOARD_ARROW_DOWN = require('../../../assets/keyboard_arrow_down.xml');
|
|
25
|
+
|
|
26
|
+
// M3 large-corner token (16dp) — the Expressive expandable-list-item pattern.
|
|
27
|
+
const CONTAINER_SHAPE = Shapes.RoundedCorner(16);
|
|
28
|
+
|
|
29
|
+
// expandVertically + fadeIn keeps motion strictly vertical.
|
|
30
|
+
// (Compose's default expandIn includes a horizontal component.)
|
|
31
|
+
const ENTER = EnterTransition.expandVertically().plus(EnterTransition.fadeIn());
|
|
32
|
+
const EXIT = ExitTransition.shrinkVertically().plus(ExitTransition.fadeOut());
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Android implementation of `Collapsible`.
|
|
36
|
+
* A rounded M3 card whose container tint fades between `transparent` (collapsed) and `surfaceContainer` (expanded).
|
|
37
|
+
*/
|
|
38
|
+
export function Collapsible({ isOpen, onOpenChange, label = '', children }: CollapsibleProps) {
|
|
39
|
+
const colors = useMaterialColors();
|
|
40
|
+
const containerColor = isOpen ? colors.surfaceContainer : 'transparent';
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Column
|
|
44
|
+
modifiers={[
|
|
45
|
+
// `clip` first so background paint and the inner ListItem's ripple both respect the rounded shape.
|
|
46
|
+
clip(CONTAINER_SHAPE),
|
|
47
|
+
background(containerColor, { animationSpec: spring() }),
|
|
48
|
+
]}>
|
|
49
|
+
<ListItem
|
|
50
|
+
// Transparent so the outer `background` is the sole tint source.
|
|
51
|
+
colors={{ containerColor: 'transparent' }}
|
|
52
|
+
modifiers={[clickable(() => onOpenChange(!isOpen))]}>
|
|
53
|
+
<ListItem.HeadlineContent>
|
|
54
|
+
<Text>{label}</Text>
|
|
55
|
+
</ListItem.HeadlineContent>
|
|
56
|
+
<ListItem.TrailingContent>
|
|
57
|
+
<Icon
|
|
58
|
+
source={KEYBOARD_ARROW_DOWN}
|
|
59
|
+
modifiers={[graphicsLayer({ rotationZ: animated(isOpen ? 180 : 0, spring()) })]}
|
|
60
|
+
/>
|
|
61
|
+
</ListItem.TrailingContent>
|
|
62
|
+
</ListItem>
|
|
63
|
+
<AnimatedVisibility visible={isOpen} enterTransition={ENTER} exitTransition={EXIT}>
|
|
64
|
+
{/* 16dp matches the M3 large-corner radius.
|
|
65
|
+
ListItem's own bottom padding adds the M3 header–body separation on top. */}
|
|
66
|
+
<Column modifiers={[padding(16, 16, 16, 16)]}>{children}</Column>
|
|
67
|
+
</AnimatedVisibility>
|
|
68
|
+
</Column>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export * from './types';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DisclosureGroup } from '@expo/ui/swift-ui';
|
|
2
|
+
|
|
3
|
+
import type { CollapsibleProps } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* iOS implementation of `Collapsible`. Wraps SwiftUI's `DisclosureGroup`.
|
|
7
|
+
*/
|
|
8
|
+
export function Collapsible({ isOpen, onOpenChange, label = '', children }: CollapsibleProps) {
|
|
9
|
+
return (
|
|
10
|
+
<DisclosureGroup label={label} isExpanded={isOpen} onIsExpandedChange={onOpenChange}>
|
|
11
|
+
{children}
|
|
12
|
+
</DisclosureGroup>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export * from './types';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ComponentProps, SyntheticEvent } from 'react';
|
|
2
|
+
import { StyleSheet, Text, unstable_createElement, View, type ViewProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { CollapsibleProps } from './types';
|
|
5
|
+
|
|
6
|
+
const Details = (
|
|
7
|
+
props: Omit<ComponentProps<'details'>, 'style'> & { style?: ViewProps['style'] }
|
|
8
|
+
) => unstable_createElement('details', props);
|
|
9
|
+
|
|
10
|
+
const Summary = (
|
|
11
|
+
props: Omit<ComponentProps<'summary'>, 'style'> & { style?: ViewProps['style'] }
|
|
12
|
+
) => unstable_createElement('summary', props);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A primitive that toggles visibility of its content via a labelled tappable
|
|
16
|
+
* header. Controlled via `isOpen` + `onOpenChange`.
|
|
17
|
+
*/
|
|
18
|
+
export function Collapsible({ isOpen, onOpenChange, label = '', children }: CollapsibleProps) {
|
|
19
|
+
return (
|
|
20
|
+
<Details
|
|
21
|
+
open={isOpen}
|
|
22
|
+
onToggle={(event: SyntheticEvent<HTMLDetailsElement>) => {
|
|
23
|
+
const nextOpen = event.currentTarget.open;
|
|
24
|
+
if (nextOpen !== isOpen) {
|
|
25
|
+
onOpenChange(nextOpen);
|
|
26
|
+
}
|
|
27
|
+
}}
|
|
28
|
+
style={styles.container}>
|
|
29
|
+
<Summary style={styles.summary}>
|
|
30
|
+
<Text>{label}</Text>
|
|
31
|
+
</Summary>
|
|
32
|
+
<View style={styles.content}>{children}</View>
|
|
33
|
+
</Details>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const styles = StyleSheet.create({
|
|
38
|
+
container: {
|
|
39
|
+
flexDirection: 'column',
|
|
40
|
+
width: '100%',
|
|
41
|
+
},
|
|
42
|
+
summary: {
|
|
43
|
+
flexDirection: 'row',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
gap: 8,
|
|
46
|
+
width: '100%',
|
|
47
|
+
paddingHorizontal: 16,
|
|
48
|
+
paddingVertical: 12,
|
|
49
|
+
userSelect: 'none',
|
|
50
|
+
cursor: 'pointer',
|
|
51
|
+
},
|
|
52
|
+
content: {
|
|
53
|
+
paddingHorizontal: 16,
|
|
54
|
+
paddingBottom: 12,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export * from './types';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props for the [`Collapsible`](#collapsible) component, a primitive that
|
|
3
|
+
* shows or hides its content with a tap on a labelled header.
|
|
4
|
+
*/
|
|
5
|
+
export interface CollapsibleProps {
|
|
6
|
+
/**
|
|
7
|
+
* Whether the content is currently expanded.
|
|
8
|
+
*/
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Called when the user taps the header to toggle the open state.
|
|
13
|
+
*/
|
|
14
|
+
onOpenChange: (isOpen: boolean) => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Text rendered in the tappable header.
|
|
18
|
+
*/
|
|
19
|
+
label?: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Content rendered when `isOpen` is `true`.
|
|
23
|
+
*/
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
}
|
|
@@ -5,7 +5,9 @@ import { useUniversalLifecycle } from '../hooks';
|
|
|
5
5
|
import type { UniversalAlignment } from '../types';
|
|
6
6
|
|
|
7
7
|
const styles = StyleSheet.create({
|
|
8
|
-
|
|
8
|
+
// `alignSelf: 'stretch'` — match SwiftUI `VStack` / Compose `Column`, which fill their parent's cross-axis.
|
|
9
|
+
// Without this, a nested Column inherits its parent's `alignItems` (often `flex-start`) and shrinks to content.
|
|
10
|
+
column: { flexDirection: 'column', alignSelf: 'stretch' },
|
|
9
11
|
hidden: { display: 'none' },
|
|
10
12
|
disabled: {
|
|
11
13
|
opacity: 0.5,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { StyleSheet, View, type LayoutChangeEvent
|
|
1
|
+
import { StyleSheet, View, type LayoutChangeEvent } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import type { UniversalHostProps } from './types';
|
|
2
4
|
|
|
3
5
|
const styles = StyleSheet.create({
|
|
4
6
|
matchContents: {
|
|
@@ -22,16 +24,10 @@ const styles = StyleSheet.create({
|
|
|
22
24
|
},
|
|
23
25
|
});
|
|
24
26
|
|
|
25
|
-
type HostProps = {
|
|
26
|
-
ignoreSafeArea?: 'all' | 'keyboard';
|
|
27
|
-
layoutDirection?: 'leftToRight' | 'rightToLeft';
|
|
28
|
-
matchContents?: boolean | { horizontal?: boolean; vertical?: boolean };
|
|
29
|
-
onLayoutContent?: (event: { nativeEvent: { width: number; height: number } }) => void;
|
|
30
|
-
useViewportSizeMeasurement?: boolean;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
27
|
/**
|
|
34
28
|
* A bridging container that hosts SwiftUI views on iOS and Jetpack Compose views on Android.
|
|
29
|
+
* On platforms without a native UI-toolkit binding (web, RN fallback), renders a plain `View`.
|
|
30
|
+
* The `colorScheme`, `layoutDirection`, and `matchContents` props are accepted for API parity but have no effect.
|
|
35
31
|
*/
|
|
36
32
|
export function Host({
|
|
37
33
|
children,
|
|
@@ -42,8 +38,9 @@ export function Host({
|
|
|
42
38
|
onLayoutContent,
|
|
43
39
|
style,
|
|
44
40
|
useViewportSizeMeasurement = false,
|
|
41
|
+
colorScheme: _colorScheme,
|
|
45
42
|
...rest
|
|
46
|
-
}:
|
|
43
|
+
}: UniversalHostProps) {
|
|
47
44
|
const shouldMatchContents =
|
|
48
45
|
typeof matchContents === 'object'
|
|
49
46
|
? matchContents.horizontal || matchContents.vertical
|
|
@@ -81,3 +78,5 @@ export function Host({
|
|
|
81
78
|
</View>
|
|
82
79
|
);
|
|
83
80
|
}
|
|
81
|
+
|
|
82
|
+
export type { UniversalHostProps } from './types';
|