@lodev09/react-native-true-sheet 4.0.0-beta.2 → 4.0.0-beta.21
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/README.md +25 -4
- package/android/build.gradle +22 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +124 -50
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +369 -163
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +59 -12
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +126 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
- package/android/src/main/jni/TrueSheetSpec.h +1 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
- package/ios/TrueSheetContainerView.h +27 -7
- package/ios/TrueSheetContainerView.mm +31 -11
- package/ios/TrueSheetContentView.h +43 -15
- package/ios/TrueSheetContentView.mm +211 -189
- package/ios/TrueSheetFooterView.h +6 -0
- package/ios/TrueSheetFooterView.mm +93 -17
- package/ios/TrueSheetModule.h +2 -7
- package/ios/TrueSheetModule.mm +7 -16
- package/ios/TrueSheetOverlayView.h +32 -0
- package/ios/TrueSheetOverlayView.mm +161 -0
- package/ios/TrueSheetView.mm +275 -56
- package/ios/TrueSheetViewController.h +31 -3
- package/ios/TrueSheetViewController.mm +393 -94
- package/ios/core/TrueSheetBlurView.h +0 -2
- package/ios/core/TrueSheetBlurView.mm +3 -36
- package/ios/core/TrueSheetDetentCalculator.h +7 -1
- package/ios/core/TrueSheetDetentCalculator.mm +27 -4
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
- package/ios/core/TrueSheetOverlayContainerView.h +31 -0
- package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
- package/ios/events/TrueSheetLifecycleEvents.h +2 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
- package/ios/tvos/TrueSheetStubs.mm +12 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
- package/lib/module/TrueSheet.js +175 -39
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +220 -243
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetBackground.js +15 -0
- package/lib/module/TrueSheetBackground.js.map +1 -0
- package/lib/module/TrueSheetOverlay.js +30 -0
- package/lib/module/TrueSheetOverlay.js.map +1 -0
- package/lib/module/TrueSheetOverlay.web.js +35 -0
- package/lib/module/TrueSheetOverlay.web.js.map +1 -0
- package/lib/module/TrueSheetPeek.web.js +20 -15
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/detentBackgrounds.js +21 -0
- package/lib/module/detentBackgrounds.js.map +1 -0
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +15 -10
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +18 -2
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/createTrueSheetRouter.js +0 -9
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
- package/lib/module/navigation/expo-router/base-types.js +2 -0
- package/lib/module/navigation/expo-router/base-types.js.map +1 -0
- package/lib/module/navigation/expo-router/index.js +10 -7
- package/lib/module/navigation/expo-router/index.js.map +1 -1
- package/lib/module/navigation/expo-router/types.js +4 -0
- package/lib/module/navigation/expo-router/types.js.map +1 -0
- package/lib/module/navigation/index.js.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/web/constants.js +1 -1
- package/lib/module/web/constants.js.map +1 -1
- package/lib/module/web/layout.js +168 -0
- package/lib/module/web/layout.js.map +1 -0
- package/lib/module/web/vaul/constants.js +9 -0
- package/lib/module/web/vaul/constants.js.map +1 -1
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/helpers.js +1 -0
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +47 -7
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +21 -10
- package/lib/module/web/vaul/use-snap-points.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +6 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +157 -34
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
- package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
- package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +12 -7
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +4 -0
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/base-types.d.ts +9 -0
- package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +2 -13
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts +8 -6
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +3 -4
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +41 -5
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/web/constants.d.ts +1 -1
- package/lib/typescript/src/web/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/layout.d.ts +19 -0
- package/lib/typescript/src/web/layout.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +13 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts +2 -1
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
- package/package.json +17 -8
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +208 -48
- package/src/TrueSheet.types.ts +166 -35
- package/src/TrueSheet.web.tsx +272 -290
- package/src/TrueSheetBackground.tsx +17 -0
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +25 -23
- package/src/detentBackgrounds.ts +24 -0
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +15 -10
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +17 -2
- package/src/mocks/navigationExpoRouter.ts +6 -5
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +5 -33
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/index.ts +28 -30
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +6 -7
- package/src/navigation/screen/useSheetScreenState.ts +2 -0
- package/src/navigation/types.ts +75 -22
- package/src/web/constants.ts +1 -1
- package/src/web/layout.ts +196 -0
- package/src/web/vaul/constants.ts +9 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +5 -0
- package/src/web/vaul/index.tsx +75 -5
- package/src/web/vaul/use-snap-points.ts +32 -15
package/src/navigation/index.ts
CHANGED
|
@@ -4,16 +4,17 @@ import {
|
|
|
4
4
|
type StandardNavigationTypeBagBase,
|
|
5
5
|
} from '@react-navigation/native';
|
|
6
6
|
|
|
7
|
-
import { createTrueSheetRouter
|
|
7
|
+
import { createTrueSheetRouter } from './createTrueSheetRouter';
|
|
8
8
|
import { trueSheetNavigator } from './navigator';
|
|
9
9
|
import type {
|
|
10
|
+
StackRouterFactory,
|
|
10
11
|
TrueSheetActionHelpers,
|
|
11
12
|
TrueSheetNavigationOptions,
|
|
12
13
|
TrueSheetNavigationEventMap,
|
|
13
14
|
TrueSheetNavigationState,
|
|
14
15
|
TrueSheetNavigatorContentExtraProps,
|
|
16
|
+
TrueSheetRouterOptions,
|
|
15
17
|
} from './types';
|
|
16
|
-
import type { TrueSheetRouterOptions } from './createTrueSheetRouter';
|
|
17
18
|
|
|
18
19
|
export interface TrueSheetNavigationTypeBag extends StandardNavigationTypeBagBase {
|
|
19
20
|
State: TrueSheetNavigationState<this['ParamList']>;
|
|
@@ -62,20 +63,18 @@ export const createTrueSheetNavigator = createNavigator;
|
|
|
62
63
|
export const createTrueSheetScreen = createScreen;
|
|
63
64
|
|
|
64
65
|
export { TrueSheetActions, type TrueSheetActionType } from './actions';
|
|
65
|
-
export {
|
|
66
|
-
createTrueSheetRouter,
|
|
67
|
-
type StackRouterFactory,
|
|
68
|
-
type TrueSheetRouterOptions,
|
|
69
|
-
} from './createTrueSheetRouter';
|
|
66
|
+
export { createTrueSheetRouter } from './createTrueSheetRouter';
|
|
70
67
|
export { useTrueSheetNavigation } from './useTrueSheetNavigation';
|
|
71
68
|
|
|
72
69
|
export type { DetentInfoEventPayload, PositionChangeEventPayload } from '../TrueSheet.types';
|
|
73
70
|
|
|
74
71
|
export type {
|
|
72
|
+
StackRouterFactory,
|
|
75
73
|
TrueSheetNavigationEventMap,
|
|
76
74
|
TrueSheetNavigationHelpers,
|
|
77
75
|
TrueSheetNavigationOptions,
|
|
78
76
|
TrueSheetNavigationProp,
|
|
79
77
|
TrueSheetNavigationState,
|
|
78
|
+
TrueSheetRouterOptions,
|
|
80
79
|
TrueSheetScreenProps,
|
|
81
80
|
} from './types';
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
PositionChangeEventPayload,
|
|
15
15
|
WillBlurEvent,
|
|
16
16
|
WillDismissEvent,
|
|
17
|
+
DismissAttemptEvent,
|
|
17
18
|
WillFocusEvent,
|
|
18
19
|
WillPresentEvent,
|
|
19
20
|
} from '../../TrueSheet.types';
|
|
@@ -78,6 +79,7 @@ export const useSheetScreenState = (props: UseSheetScreenStateProps) => {
|
|
|
78
79
|
onDidPresent: (e: DidPresentEvent) => emitEvent('sheetDidPresent', e.nativeEvent),
|
|
79
80
|
onWillDismiss: (_e: WillDismissEvent) => emitEvent('sheetWillDismiss', undefined),
|
|
80
81
|
onDidDismiss,
|
|
82
|
+
onDismissAttempt: (_e: DismissAttemptEvent) => emitEvent('sheetDismissAttempt', undefined),
|
|
81
83
|
onDetentChange: (e: DetentChangeEvent) => emitEvent('sheetDetentChange', e.nativeEvent),
|
|
82
84
|
onDragBegin: (e: DragBeginEvent) => emitEvent('sheetDragBegin', e.nativeEvent),
|
|
83
85
|
onDragChange: (e: DragChangeEvent) => emitEvent('sheetDragChange', e.nativeEvent),
|
package/src/navigation/types.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
+
import type { NavigatorArgs } from 'standard-navigation';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
DetentInfoEventPayload,
|
|
5
|
+
PositionChangeEventPayload,
|
|
6
|
+
TrueSheetProps,
|
|
7
|
+
} from '../TrueSheet.types';
|
|
8
|
+
import type { TrueSheetActionType } from './actions';
|
|
1
9
|
import type {
|
|
2
10
|
DefaultNavigatorOptions,
|
|
3
11
|
Descriptor,
|
|
12
|
+
NavigationAction,
|
|
4
13
|
NavigationHelpers,
|
|
5
14
|
NavigationProp,
|
|
6
15
|
NavigationState,
|
|
7
16
|
ParamListBase,
|
|
8
17
|
RouteProp,
|
|
18
|
+
Router,
|
|
9
19
|
StackActionHelpers,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import type {
|
|
14
|
-
DetentInfoEventPayload,
|
|
15
|
-
PositionChangeEventPayload,
|
|
16
|
-
TrueSheetProps,
|
|
17
|
-
} from '../TrueSheet.types';
|
|
18
|
-
import type { TrueSheetActionType } from './actions';
|
|
20
|
+
StackRouterOptions,
|
|
21
|
+
} from './base-types';
|
|
19
22
|
|
|
20
23
|
export type PositionChangeHandler = (payload: PositionChangeEventPayload) => void;
|
|
21
24
|
|
|
@@ -36,6 +39,10 @@ export type TrueSheetNavigationEventMap = {
|
|
|
36
39
|
* Event fired when the sheet has been dismissed.
|
|
37
40
|
*/
|
|
38
41
|
sheetDidDismiss: { data: undefined };
|
|
42
|
+
/**
|
|
43
|
+
* Event fired when the user tries to dismiss a non-dismissible sheet.
|
|
44
|
+
*/
|
|
45
|
+
sheetDismissAttempt: { data: undefined };
|
|
39
46
|
/**
|
|
40
47
|
* Event fired when the sheet's detent changes.
|
|
41
48
|
*/
|
|
@@ -74,18 +81,54 @@ export type TrueSheetNavigationEventMap = {
|
|
|
74
81
|
sheetDidBlur: { data: undefined };
|
|
75
82
|
};
|
|
76
83
|
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
/**
|
|
85
|
+
* A navigation state tagged as `true-sheet`, with the sheet fields the router
|
|
86
|
+
* adds to each route.
|
|
87
|
+
* Generic over the base state so each entry point can bind its own
|
|
88
|
+
* `NavigationState` — see `./base-types`.
|
|
89
|
+
*/
|
|
90
|
+
export type TrueSheetStateOf<State extends { routes: readonly unknown[] }> = Omit<
|
|
91
|
+
State,
|
|
79
92
|
'routes'
|
|
80
93
|
> & {
|
|
81
94
|
type: 'true-sheet';
|
|
82
|
-
routes: (
|
|
95
|
+
routes: (State['routes'][number] & {
|
|
83
96
|
resizeIndex?: number;
|
|
84
97
|
resizeKey?: number;
|
|
85
98
|
closing?: boolean;
|
|
86
99
|
})[];
|
|
87
100
|
};
|
|
88
101
|
|
|
102
|
+
export type TrueSheetNavigationState<ParamList extends ParamListBase> = TrueSheetStateOf<
|
|
103
|
+
NavigationState<ParamList>
|
|
104
|
+
>;
|
|
105
|
+
|
|
106
|
+
export type TrueSheetRouterState = TrueSheetNavigationState<ParamListBase>;
|
|
107
|
+
|
|
108
|
+
export type TrueSheetRouterOptions = StackRouterOptions;
|
|
109
|
+
|
|
110
|
+
export type TrueSheetRouter = Router<TrueSheetRouterState, TrueSheetActionType>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The wrapped router factory — also the shape `createTrueSheetRouter` accepts,
|
|
114
|
+
* pre-instantiated so either entry point's `StackRouterFactory` fits it.
|
|
115
|
+
*/
|
|
116
|
+
export type TrueSheetRouterFactory = (options: TrueSheetRouterOptions) => TrueSheetRouter;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The base `StackRouter` factory to wrap, typed generically over the state and
|
|
120
|
+
* action types the wrapper threads through — `StackRouter` is structural and
|
|
121
|
+
* never touches the sheet-specific fields.
|
|
122
|
+
* Injected by each entry point so the shared router never imports
|
|
123
|
+
* `@react-navigation/*` at runtime (Expo Router apps don't install it).
|
|
124
|
+
*/
|
|
125
|
+
export type StackRouterFactory = <
|
|
126
|
+
State extends NavigationState<ParamListBase>,
|
|
127
|
+
Action extends NavigationAction,
|
|
128
|
+
>(
|
|
129
|
+
options: TrueSheetRouterOptions
|
|
130
|
+
) => Router<State, Action>;
|
|
131
|
+
|
|
89
132
|
/**
|
|
90
133
|
* `TrueSheetNavigationEventMap` in the `standard-navigation` event-map shape.
|
|
91
134
|
*/
|
|
@@ -116,13 +159,20 @@ export interface TrueSheetNavigatorContentExtraProps {
|
|
|
116
159
|
dispatch: TrueSheetDispatch;
|
|
117
160
|
}
|
|
118
161
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Generic over the base helpers so each entry point can bind its own
|
|
164
|
+
* `StackActionHelpers` — see `./base-types`.
|
|
165
|
+
*/
|
|
166
|
+
export type TrueSheetActionHelpersOf<Helpers> = Helpers & {
|
|
167
|
+
/**
|
|
168
|
+
* Resize the sheet to a specific detent index.
|
|
169
|
+
*/
|
|
170
|
+
resize(index?: number): void;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export type TrueSheetActionHelpers<ParamList extends ParamListBase> = TrueSheetActionHelpersOf<
|
|
174
|
+
StackActionHelpers<ParamList>
|
|
175
|
+
>;
|
|
126
176
|
|
|
127
177
|
export type TrueSheetNavigationProp<
|
|
128
178
|
ParamList extends ParamListBase,
|
|
@@ -156,6 +206,9 @@ export type TrueSheetNavigationSheetProps = Pick<
|
|
|
156
206
|
TrueSheetProps,
|
|
157
207
|
| 'detents'
|
|
158
208
|
| 'backgroundColor'
|
|
209
|
+
| 'detentBackgrounds'
|
|
210
|
+
| 'background'
|
|
211
|
+
| 'backgroundStyle'
|
|
159
212
|
| 'cornerRadius'
|
|
160
213
|
| 'dismissible'
|
|
161
214
|
| 'draggable'
|
|
@@ -165,7 +218,6 @@ export type TrueSheetNavigationSheetProps = Pick<
|
|
|
165
218
|
| 'dimmed'
|
|
166
219
|
| 'dimmedDetentIndex'
|
|
167
220
|
| 'backgroundBlur'
|
|
168
|
-
| 'blurOptions'
|
|
169
221
|
| 'maxContentHeight'
|
|
170
222
|
| 'maxContentWidth'
|
|
171
223
|
| 'presentation'
|
|
@@ -176,10 +228,11 @@ export type TrueSheetNavigationSheetProps = Pick<
|
|
|
176
228
|
| 'footer'
|
|
177
229
|
| 'footerStyle'
|
|
178
230
|
| 'footerOptions'
|
|
231
|
+
| 'scrollableRef'
|
|
179
232
|
| 'scrollableOptions'
|
|
180
233
|
| 'insetAdjustment'
|
|
181
|
-
| '
|
|
182
|
-
| '
|
|
234
|
+
| 'placement'
|
|
235
|
+
| 'placementOffset'
|
|
183
236
|
| 'elevation'
|
|
184
237
|
| 'detached'
|
|
185
238
|
| 'detachedOffset'
|
package/src/web/constants.ts
CHANGED
|
@@ -4,7 +4,7 @@ export const DEFAULT_FORM_SHEET_WIDTH = 580;
|
|
|
4
4
|
export const DEFAULT_FORM_SHEET_HEIGHT_RATIO = 0.5;
|
|
5
5
|
export const COLOR_SURFACE_CONTAINER_LOW_LIGHT = '#F7F2FA';
|
|
6
6
|
export const COLOR_SURFACE_CONTAINER_LOW_DARK = '#1D1B20';
|
|
7
|
-
export const
|
|
7
|
+
export const DEFAULT_PLACEMENT_OFFSET = 16;
|
|
8
8
|
export const DEFAULT_DETACHED_OFFSET = 16;
|
|
9
9
|
export const DEFAULT_GRABBER_COLOR_LIGHT = 'rgba(0, 0, 0, 0.3)';
|
|
10
10
|
export const DEFAULT_GRABBER_COLOR_DARK = 'rgba(255, 255, 255, 0.3)';
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
3
|
+
const FOOTER_INSET = '--truesheet-footer-inset';
|
|
4
|
+
|
|
5
|
+
interface SheetLayoutOptions {
|
|
6
|
+
drawer: HTMLElement;
|
|
7
|
+
layout: HTMLElement;
|
|
8
|
+
getContent: () => HTMLElement | null;
|
|
9
|
+
getHeader: () => HTMLElement | null;
|
|
10
|
+
getFooter: () => HTMLElement | null;
|
|
11
|
+
getScrollable: () => HTMLElement | null;
|
|
12
|
+
absoluteHeader: boolean;
|
|
13
|
+
absoluteFooter: boolean;
|
|
14
|
+
footerInsetAdjustment: boolean;
|
|
15
|
+
measureContent: boolean;
|
|
16
|
+
onHeightChange: (height: number) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function observeSheetLayout({
|
|
20
|
+
drawer,
|
|
21
|
+
layout,
|
|
22
|
+
getContent,
|
|
23
|
+
getHeader,
|
|
24
|
+
getFooter,
|
|
25
|
+
getScrollable,
|
|
26
|
+
absoluteHeader,
|
|
27
|
+
absoluteFooter,
|
|
28
|
+
footerInsetAdjustment,
|
|
29
|
+
measureContent,
|
|
30
|
+
onHeightChange,
|
|
31
|
+
}: SheetLayoutOptions) {
|
|
32
|
+
let frame = 0;
|
|
33
|
+
let disposed = false;
|
|
34
|
+
let width = -1;
|
|
35
|
+
let height = -1;
|
|
36
|
+
let scroller: HTMLElement | null = null;
|
|
37
|
+
let originalPadding = '';
|
|
38
|
+
let insetPadding = '';
|
|
39
|
+
let appliedInset = 0;
|
|
40
|
+
const observed = new Set<Element>();
|
|
41
|
+
|
|
42
|
+
// This tree has no viewport constraint or injected footer inset. Its resize
|
|
43
|
+
// observer catches intrinsic changes without observing the live flex layout.
|
|
44
|
+
const measurement = measureContent ? document.createElement('div') : null;
|
|
45
|
+
if (measurement) {
|
|
46
|
+
Object.assign(measurement.style, {
|
|
47
|
+
position: 'absolute',
|
|
48
|
+
top: '0',
|
|
49
|
+
left: '0',
|
|
50
|
+
display: 'flex',
|
|
51
|
+
flexDirection: 'column',
|
|
52
|
+
visibility: 'hidden',
|
|
53
|
+
pointerEvents: 'none',
|
|
54
|
+
});
|
|
55
|
+
measurement.style.setProperty(FOOTER_INSET, '0px');
|
|
56
|
+
measurement.inert = true;
|
|
57
|
+
measurement.setAttribute('aria-hidden', 'true');
|
|
58
|
+
measurement.setAttribute('data-truesheet-measure', '');
|
|
59
|
+
drawer.appendChild(measurement);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const measure = () => {
|
|
63
|
+
if (!measurement?.isConnected) return;
|
|
64
|
+
const headerHeight = absoluteHeader ? 0 : (getHeader()?.offsetHeight ?? 0);
|
|
65
|
+
const footerHeight =
|
|
66
|
+
!absoluteFooter || (footerInsetAdjustment && scroller) ? (getFooter()?.offsetHeight ?? 0) : 0;
|
|
67
|
+
const next = measurement.offsetHeight + headerHeight + footerHeight;
|
|
68
|
+
if (height === next) return;
|
|
69
|
+
height = next;
|
|
70
|
+
onHeightChange(next);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const updateInset = () => {
|
|
74
|
+
let inset = 0;
|
|
75
|
+
const footer = getFooter();
|
|
76
|
+
if (footerInsetAdjustment && scroller && footer) {
|
|
77
|
+
let bottom = scroller.offsetTop + scroller.offsetHeight;
|
|
78
|
+
let parent = scroller.offsetParent;
|
|
79
|
+
while (parent instanceof HTMLElement && parent !== layout) {
|
|
80
|
+
bottom += parent.offsetTop;
|
|
81
|
+
parent = parent.offsetParent;
|
|
82
|
+
}
|
|
83
|
+
if (parent === layout) {
|
|
84
|
+
inset = Math.min(
|
|
85
|
+
footer.offsetHeight,
|
|
86
|
+
Math.max(0, bottom - (layout.clientHeight - footer.offsetHeight))
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (inset === appliedInset) return;
|
|
91
|
+
appliedInset = inset;
|
|
92
|
+
layout.style.setProperty(FOOTER_INSET, `${inset}px`);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
96
|
+
if (measurement && width !== layout.clientWidth) {
|
|
97
|
+
width = layout.clientWidth;
|
|
98
|
+
measurement.style.width = `${width}px`;
|
|
99
|
+
}
|
|
100
|
+
updateInset();
|
|
101
|
+
measure();
|
|
102
|
+
});
|
|
103
|
+
const measurementObserver = new ResizeObserver(measure);
|
|
104
|
+
if (measurement) measurementObserver.observe(measurement);
|
|
105
|
+
|
|
106
|
+
const restorePadding = () => {
|
|
107
|
+
if (scroller && scroller.style.paddingBottom === insetPadding) {
|
|
108
|
+
scroller.style.paddingBottom = originalPadding;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const sync = () => {
|
|
113
|
+
if (disposed) return;
|
|
114
|
+
const content = getContent();
|
|
115
|
+
const nextScroller = getScrollable();
|
|
116
|
+
if (nextScroller !== scroller) {
|
|
117
|
+
restorePadding();
|
|
118
|
+
scroller = nextScroller;
|
|
119
|
+
insetPadding = '';
|
|
120
|
+
}
|
|
121
|
+
if (
|
|
122
|
+
footerInsetAdjustment &&
|
|
123
|
+
scroller &&
|
|
124
|
+
(!insetPadding || scroller.style.paddingBottom !== insetPadding)
|
|
125
|
+
) {
|
|
126
|
+
originalPadding = scroller.style.paddingBottom;
|
|
127
|
+
const base = getComputedStyle(scroller).paddingBottom;
|
|
128
|
+
insetPadding = `calc(${base} + var(${FOOTER_INSET}, 0px))`;
|
|
129
|
+
scroller.style.paddingBottom = insetPadding;
|
|
130
|
+
insetPadding = scroller.style.paddingBottom;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const nodes = new Set<Element>([layout]);
|
|
134
|
+
for (const node of [getHeader(), getFooter(), scroller]) {
|
|
135
|
+
if (node) nodes.add(node);
|
|
136
|
+
}
|
|
137
|
+
for (const node of observed) {
|
|
138
|
+
if (!nodes.has(node)) {
|
|
139
|
+
resizeObserver.unobserve(node);
|
|
140
|
+
observed.delete(node);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const node of nodes) {
|
|
144
|
+
if (!observed.has(node)) {
|
|
145
|
+
resizeObserver.observe(node);
|
|
146
|
+
observed.add(node);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (measurement && content) {
|
|
151
|
+
width = layout.clientWidth;
|
|
152
|
+
measurement.style.width = `${width}px`;
|
|
153
|
+
measurement.replaceChildren(content.cloneNode(true));
|
|
154
|
+
}
|
|
155
|
+
updateInset();
|
|
156
|
+
measure();
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const mutationObserver = new MutationObserver((mutations) => {
|
|
160
|
+
const changed = mutations.some(
|
|
161
|
+
(mutation) => mutation.target !== layout || mutation.type === 'childList'
|
|
162
|
+
);
|
|
163
|
+
if (!changed || frame) return;
|
|
164
|
+
frame = requestAnimationFrame(() => {
|
|
165
|
+
frame = 0;
|
|
166
|
+
sync();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
sync();
|
|
171
|
+
mutationObserver.observe(layout, {
|
|
172
|
+
childList: true,
|
|
173
|
+
characterData: true,
|
|
174
|
+
attributes: true,
|
|
175
|
+
attributeFilter: ['style', 'class'],
|
|
176
|
+
subtree: true,
|
|
177
|
+
});
|
|
178
|
+
const footer = getFooter();
|
|
179
|
+
if (absoluteFooter && footer) {
|
|
180
|
+
mutationObserver.observe(footer, { childList: true, characterData: true, subtree: true });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
measure,
|
|
185
|
+
disconnect: () => {
|
|
186
|
+
disposed = true;
|
|
187
|
+
cancelAnimationFrame(frame);
|
|
188
|
+
mutationObserver.disconnect();
|
|
189
|
+
resizeObserver.disconnect();
|
|
190
|
+
measurementObserver.disconnect();
|
|
191
|
+
restorePadding();
|
|
192
|
+
layout.style.removeProperty(FOOTER_INSET);
|
|
193
|
+
measurement?.remove();
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -9,6 +9,15 @@ export const DEFAULT_PEEK_HEIGHT = 150;
|
|
|
9
9
|
|
|
10
10
|
export const CLOSE_THRESHOLD = 0.25;
|
|
11
11
|
|
|
12
|
+
// Compose Material3 SheetState's dismiss thresholds, used by the 'short'
|
|
13
|
+
// dismiss threshold: velocity in px/ms, distance in px.
|
|
14
|
+
export const SHORT_DISMISS_VELOCITY = 0.125;
|
|
15
|
+
export const SHORT_DISMISS_DISTANCE = 56;
|
|
16
|
+
|
|
17
|
+
// Minimum downward pull (px) on a non-dismissible drawer at its first snap
|
|
18
|
+
// point before the release counts as a dismiss attempt.
|
|
19
|
+
export const DISMISS_ATTEMPT_THRESHOLD = 24;
|
|
20
|
+
|
|
12
21
|
export const SCROLL_LOCK_TIMEOUT = 100;
|
|
13
22
|
|
|
14
23
|
export const BORDER_RADIUS = 8;
|
package/src/web/vaul/context.ts
CHANGED
|
@@ -23,6 +23,7 @@ interface DrawerContextValue {
|
|
|
23
23
|
onNestedOpenChange: (o: boolean) => void;
|
|
24
24
|
onNestedRelease: (event: React.PointerEvent<HTMLDivElement>, open: boolean) => void;
|
|
25
25
|
dismissible: boolean;
|
|
26
|
+
onDismissAttempt?: () => void;
|
|
26
27
|
isOpen: boolean;
|
|
27
28
|
isDragging: boolean;
|
|
28
29
|
keyboardIsOpen: React.MutableRefObject<boolean>;
|
package/src/web/vaul/helpers.ts
CHANGED
|
@@ -80,6 +80,11 @@ export const isVertical = (direction: DrawerDirection) => {
|
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
+
export const getSnapPointTransform = (direction: DrawerDirection) =>
|
|
84
|
+
isVertical(direction)
|
|
85
|
+
? 'translate3d(0, var(--snap-point-height), 0)'
|
|
86
|
+
: 'translate3d(var(--snap-point-height), 0, 0)';
|
|
87
|
+
|
|
83
88
|
export function getTranslate(element: HTMLElement, direction: DrawerDirection): number | null {
|
|
84
89
|
if (!element) {
|
|
85
90
|
return null;
|
package/src/web/vaul/index.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import { isIOS, isMobileFirefox } from './browser';
|
|
|
12
12
|
import {
|
|
13
13
|
BORDER_RADIUS,
|
|
14
14
|
CLOSE_THRESHOLD,
|
|
15
|
+
DISMISS_ATTEMPT_THRESHOLD,
|
|
15
16
|
DRAG_CLASS,
|
|
16
17
|
NESTED_DISPLACEMENT,
|
|
17
18
|
SCROLL_LOCK_TIMEOUT,
|
|
@@ -19,7 +20,14 @@ import {
|
|
|
19
20
|
VELOCITY_THRESHOLD,
|
|
20
21
|
WINDOW_TOP_OFFSET,
|
|
21
22
|
} from './constants';
|
|
22
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
dampenValue,
|
|
25
|
+
getSnapPointTransform,
|
|
26
|
+
getTranslate,
|
|
27
|
+
isVertical,
|
|
28
|
+
reset,
|
|
29
|
+
set,
|
|
30
|
+
} from './helpers';
|
|
23
31
|
import type { DrawerDirection } from './types';
|
|
24
32
|
import { useComposedRefs } from './use-composed-refs';
|
|
25
33
|
import { useControllableState } from './use-controllable-state';
|
|
@@ -93,6 +101,18 @@ export type DialogProps = {
|
|
|
93
101
|
* @default true
|
|
94
102
|
*/
|
|
95
103
|
dismissible?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* How far the drawer must be dragged past its first snap point to close.
|
|
106
|
+
* `'half'` closes past half of it, `'short'` on a short drag or any fling.
|
|
107
|
+
* @default 'half'
|
|
108
|
+
*/
|
|
109
|
+
dismissThreshold?: 'half' | 'short';
|
|
110
|
+
/**
|
|
111
|
+
* Called when the user tries to close a non-dismissible drawer — dragging down
|
|
112
|
+
* at the first snap point, pressing escape, or tapping the handle of a
|
|
113
|
+
* single-snap-point drawer. Overlay clicks don't count, mirroring iOS.
|
|
114
|
+
*/
|
|
115
|
+
onDismissAttempt?: () => void;
|
|
96
116
|
/**
|
|
97
117
|
* When `false` the drawer can't be dragged by the user. Programmatic snap-point
|
|
98
118
|
* changes still animate. Defaults to `true`.
|
|
@@ -218,6 +238,8 @@ export function Root({
|
|
|
218
238
|
closeThreshold = CLOSE_THRESHOLD,
|
|
219
239
|
scrollLockTimeout = SCROLL_LOCK_TIMEOUT,
|
|
220
240
|
dismissible = true,
|
|
241
|
+
dismissThreshold = 'half',
|
|
242
|
+
onDismissAttempt,
|
|
221
243
|
draggable = true,
|
|
222
244
|
handleOnly = false,
|
|
223
245
|
fadeFromIndex = snapPoints && snapPoints.length - 1,
|
|
@@ -274,6 +296,7 @@ export function Root({
|
|
|
274
296
|
const isAllowedToDrag = React.useRef<boolean>(false);
|
|
275
297
|
const nestedOpenChangeTimer = React.useRef<NodeJS.Timeout | null>(null);
|
|
276
298
|
const pointerStart = React.useRef(0);
|
|
299
|
+
const dismissAttempted = React.useRef(false);
|
|
277
300
|
const keyboardIsOpen = React.useRef(false);
|
|
278
301
|
const shouldAnimate = React.useRef(!defaultOpen);
|
|
279
302
|
const previousDiffFromInitial = React.useRef(0);
|
|
@@ -517,7 +540,17 @@ export function Root({
|
|
|
517
540
|
const noCloseSnapPointsPreCondition = snapPoints && !dismissible && !isDraggingInDirection;
|
|
518
541
|
|
|
519
542
|
// Disallow dragging down to close when first snap point is the active one and dismissible prop is set to false.
|
|
520
|
-
if (noCloseSnapPointsPreCondition && activeSnapPointIndex === 0)
|
|
543
|
+
if (noCloseSnapPointsPreCondition && activeSnapPointIndex === 0) {
|
|
544
|
+
// The drawer stays put. Remember a deliberate pull that would otherwise
|
|
545
|
+
// have dragged it (content at its top) so release() can report it.
|
|
546
|
+
if (
|
|
547
|
+
-draggedDistance > DISMISS_ATTEMPT_THRESHOLD &&
|
|
548
|
+
(isAllowedToDrag.current || shouldDrag(event.target, isDraggingInDirection))
|
|
549
|
+
) {
|
|
550
|
+
dismissAttempted.current = true;
|
|
551
|
+
}
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
521
554
|
|
|
522
555
|
// We need to capture last time when drag with scroll was triggered and have a timeout between
|
|
523
556
|
const absDraggedDistance = Math.abs(draggedDistance);
|
|
@@ -778,6 +811,21 @@ export function Root({
|
|
|
778
811
|
unfreezeScrollables();
|
|
779
812
|
setIsDragging(false);
|
|
780
813
|
dragEndTime.current = new Date();
|
|
814
|
+
|
|
815
|
+
if (dismissAttempted.current) {
|
|
816
|
+
dismissAttempted.current = false;
|
|
817
|
+
if (isOpen) onDismissAttempt?.();
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// The press that started this "drag" may have dismissed the drawer (e.g. a
|
|
821
|
+
// back button acting on pointerdown). Snapping now would overwrite the
|
|
822
|
+
// dismiss transform that closeDrawer just wrote and freeze the sheet in
|
|
823
|
+
// place until it unmounts.
|
|
824
|
+
if (!isOpen) {
|
|
825
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, false);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
|
|
781
829
|
const swipeAmount = getTranslate(drawerRef.current, direction);
|
|
782
830
|
|
|
783
831
|
// Drag overshoot below the lowest snap point translates the detached
|
|
@@ -814,6 +862,7 @@ export function Root({
|
|
|
814
862
|
closeDrawer,
|
|
815
863
|
velocity,
|
|
816
864
|
dismissible,
|
|
865
|
+
dismissThreshold,
|
|
817
866
|
});
|
|
818
867
|
if (pointerEvent) onReleaseProp?.(pointerEvent, true);
|
|
819
868
|
return;
|
|
@@ -966,6 +1015,7 @@ export function Root({
|
|
|
966
1015
|
onTouchDrag: (event, pointerEvent) => drag(event, pointerEvent),
|
|
967
1016
|
onTouchRelease: (event, pointerEvent) => release(event, pointerEvent),
|
|
968
1017
|
dismissible,
|
|
1018
|
+
onDismissAttempt,
|
|
969
1019
|
shouldAnimate,
|
|
970
1020
|
handleOnly,
|
|
971
1021
|
isOpen,
|
|
@@ -1121,6 +1171,7 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1121
1171
|
snapPoints,
|
|
1122
1172
|
setActiveSnapPoint,
|
|
1123
1173
|
dismissible,
|
|
1174
|
+
onDismissAttempt,
|
|
1124
1175
|
container,
|
|
1125
1176
|
handleOnly,
|
|
1126
1177
|
shouldAnimate,
|
|
@@ -1265,11 +1316,17 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1265
1316
|
// same detent). Without this, position goes stale mid-animation because
|
|
1266
1317
|
// the drawer's `transitionrun` never fires.
|
|
1267
1318
|
const onTransitionRun = (e: TransitionEvent) => {
|
|
1268
|
-
if (
|
|
1319
|
+
if (
|
|
1320
|
+
(e.target === drawer || e.target === wrapper) &&
|
|
1321
|
+
(e.propertyName === 'transform' || e.propertyName === '--snap-point-height')
|
|
1322
|
+
)
|
|
1269
1323
|
state.start();
|
|
1270
1324
|
};
|
|
1271
1325
|
const onTransitionDone = (e: TransitionEvent) => {
|
|
1272
|
-
if (
|
|
1326
|
+
if (
|
|
1327
|
+
(e.target === drawer || e.target === wrapper) &&
|
|
1328
|
+
(e.propertyName === 'transform' || e.propertyName === '--snap-point-height')
|
|
1329
|
+
)
|
|
1273
1330
|
state.stop();
|
|
1274
1331
|
};
|
|
1275
1332
|
const onAnimationStart = (e: AnimationEvent) => {
|
|
@@ -1376,6 +1433,9 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1376
1433
|
wasOpenRef.current = isOpen;
|
|
1377
1434
|
}, [isOpen, detached, drawerRef]);
|
|
1378
1435
|
|
|
1436
|
+
// Subsequent snap writes belong to useSnapPoints. A React style update on
|
|
1437
|
+
// release would overwrite the live drag value before its transition starts.
|
|
1438
|
+
const initialSnapPointOffset = React.useRef(snapPointsOffset?.[activeSnapPointIndex ?? 0] ?? 0);
|
|
1379
1439
|
const contentNode = (
|
|
1380
1440
|
<DialogPrimitive.Content
|
|
1381
1441
|
data-vaul-drawer-direction={direction}
|
|
@@ -1390,7 +1450,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1390
1450
|
style={
|
|
1391
1451
|
snapPointsOffset && snapPointsOffset.length > 0
|
|
1392
1452
|
? ({
|
|
1393
|
-
'--snap-point-height': `${
|
|
1453
|
+
'--snap-point-height': `${initialSnapPointOffset.current}px`,
|
|
1454
|
+
'transform': getSnapPointTransform(direction),
|
|
1394
1455
|
...style,
|
|
1395
1456
|
'pointerEvents': 'auto',
|
|
1396
1457
|
} as React.CSSProperties)
|
|
@@ -1453,6 +1514,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1453
1514
|
activeSnapPointIndex > 0
|
|
1454
1515
|
) {
|
|
1455
1516
|
setActiveSnapPoint(snapPoints![0]!);
|
|
1517
|
+
} else {
|
|
1518
|
+
onDismissAttempt?.();
|
|
1456
1519
|
}
|
|
1457
1520
|
}
|
|
1458
1521
|
}}
|
|
@@ -1594,6 +1657,7 @@ export const Handle = React.forwardRef<HTMLDivElement, HandleProps>(
|
|
|
1594
1657
|
activeSnapPoint,
|
|
1595
1658
|
setActiveSnapPoint,
|
|
1596
1659
|
dismissible,
|
|
1660
|
+
onDismissAttempt,
|
|
1597
1661
|
handleOnly,
|
|
1598
1662
|
isOpen,
|
|
1599
1663
|
onPress,
|
|
@@ -1637,6 +1701,12 @@ export const Handle = React.forwardRef<HTMLDivElement, HandleProps>(
|
|
|
1637
1701
|
return;
|
|
1638
1702
|
}
|
|
1639
1703
|
|
|
1704
|
+
// Mirrors native: a single-detent sheet's handle tap is a dismiss.
|
|
1705
|
+
if (isLastSnapPoint && snapPoints.length === 1) {
|
|
1706
|
+
onDismissAttempt?.();
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1640
1710
|
const currentSnapIndex = snapPoints.findIndex((point) => point === activeSnapPoint);
|
|
1641
1711
|
if (currentSnapIndex === -1) return; // activeSnapPoint not found in snapPoints
|
|
1642
1712
|
const nextSnapPoint = snapPoints[currentSnapIndex + 1];
|