@lodev09/react-native-true-sheet 4.0.0-beta.2 → 4.0.0-beta.20
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 +302 -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 +119 -50
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +360 -163
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +54 -12
- 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 +212 -55
- 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 +173 -39
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +218 -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 +14 -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 +4 -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 +44 -6
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +6 -8
- 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 +135 -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 +11 -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 +1 -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 +7 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -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 +206 -48
- package/src/TrueSheet.types.ts +142 -35
- package/src/TrueSheet.web.tsx +270 -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 +14 -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 +4 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +5 -0
- package/src/web/vaul/index.tsx +67 -5
- package/src/web/vaul/use-snap-points.ts +6 -14
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,10 @@ export const DEFAULT_PEEK_HEIGHT = 150;
|
|
|
9
9
|
|
|
10
10
|
export const CLOSE_THRESHOLD = 0.25;
|
|
11
11
|
|
|
12
|
+
// Minimum downward pull (px) on a non-dismissible drawer at its first snap
|
|
13
|
+
// point before the release counts as a dismiss attempt.
|
|
14
|
+
export const DISMISS_ATTEMPT_THRESHOLD = 24;
|
|
15
|
+
|
|
12
16
|
export const SCROLL_LOCK_TIMEOUT = 100;
|
|
13
17
|
|
|
14
18
|
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,12 @@ export type DialogProps = {
|
|
|
93
101
|
* @default true
|
|
94
102
|
*/
|
|
95
103
|
dismissible?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Called when the user tries to close a non-dismissible drawer — dragging down
|
|
106
|
+
* at the first snap point, pressing escape, or tapping the handle of a
|
|
107
|
+
* single-snap-point drawer. Overlay clicks don't count, mirroring iOS.
|
|
108
|
+
*/
|
|
109
|
+
onDismissAttempt?: () => void;
|
|
96
110
|
/**
|
|
97
111
|
* When `false` the drawer can't be dragged by the user. Programmatic snap-point
|
|
98
112
|
* changes still animate. Defaults to `true`.
|
|
@@ -218,6 +232,7 @@ export function Root({
|
|
|
218
232
|
closeThreshold = CLOSE_THRESHOLD,
|
|
219
233
|
scrollLockTimeout = SCROLL_LOCK_TIMEOUT,
|
|
220
234
|
dismissible = true,
|
|
235
|
+
onDismissAttempt,
|
|
221
236
|
draggable = true,
|
|
222
237
|
handleOnly = false,
|
|
223
238
|
fadeFromIndex = snapPoints && snapPoints.length - 1,
|
|
@@ -274,6 +289,7 @@ export function Root({
|
|
|
274
289
|
const isAllowedToDrag = React.useRef<boolean>(false);
|
|
275
290
|
const nestedOpenChangeTimer = React.useRef<NodeJS.Timeout | null>(null);
|
|
276
291
|
const pointerStart = React.useRef(0);
|
|
292
|
+
const dismissAttempted = React.useRef(false);
|
|
277
293
|
const keyboardIsOpen = React.useRef(false);
|
|
278
294
|
const shouldAnimate = React.useRef(!defaultOpen);
|
|
279
295
|
const previousDiffFromInitial = React.useRef(0);
|
|
@@ -517,7 +533,17 @@ export function Root({
|
|
|
517
533
|
const noCloseSnapPointsPreCondition = snapPoints && !dismissible && !isDraggingInDirection;
|
|
518
534
|
|
|
519
535
|
// 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)
|
|
536
|
+
if (noCloseSnapPointsPreCondition && activeSnapPointIndex === 0) {
|
|
537
|
+
// The drawer stays put. Remember a deliberate pull that would otherwise
|
|
538
|
+
// have dragged it (content at its top) so release() can report it.
|
|
539
|
+
if (
|
|
540
|
+
-draggedDistance > DISMISS_ATTEMPT_THRESHOLD &&
|
|
541
|
+
(isAllowedToDrag.current || shouldDrag(event.target, isDraggingInDirection))
|
|
542
|
+
) {
|
|
543
|
+
dismissAttempted.current = true;
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
521
547
|
|
|
522
548
|
// We need to capture last time when drag with scroll was triggered and have a timeout between
|
|
523
549
|
const absDraggedDistance = Math.abs(draggedDistance);
|
|
@@ -778,6 +804,21 @@ export function Root({
|
|
|
778
804
|
unfreezeScrollables();
|
|
779
805
|
setIsDragging(false);
|
|
780
806
|
dragEndTime.current = new Date();
|
|
807
|
+
|
|
808
|
+
if (dismissAttempted.current) {
|
|
809
|
+
dismissAttempted.current = false;
|
|
810
|
+
if (isOpen) onDismissAttempt?.();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// The press that started this "drag" may have dismissed the drawer (e.g. a
|
|
814
|
+
// back button acting on pointerdown). Snapping now would overwrite the
|
|
815
|
+
// dismiss transform that closeDrawer just wrote and freeze the sheet in
|
|
816
|
+
// place until it unmounts.
|
|
817
|
+
if (!isOpen) {
|
|
818
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, false);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
|
|
781
822
|
const swipeAmount = getTranslate(drawerRef.current, direction);
|
|
782
823
|
|
|
783
824
|
// Drag overshoot below the lowest snap point translates the detached
|
|
@@ -966,6 +1007,7 @@ export function Root({
|
|
|
966
1007
|
onTouchDrag: (event, pointerEvent) => drag(event, pointerEvent),
|
|
967
1008
|
onTouchRelease: (event, pointerEvent) => release(event, pointerEvent),
|
|
968
1009
|
dismissible,
|
|
1010
|
+
onDismissAttempt,
|
|
969
1011
|
shouldAnimate,
|
|
970
1012
|
handleOnly,
|
|
971
1013
|
isOpen,
|
|
@@ -1121,6 +1163,7 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1121
1163
|
snapPoints,
|
|
1122
1164
|
setActiveSnapPoint,
|
|
1123
1165
|
dismissible,
|
|
1166
|
+
onDismissAttempt,
|
|
1124
1167
|
container,
|
|
1125
1168
|
handleOnly,
|
|
1126
1169
|
shouldAnimate,
|
|
@@ -1265,11 +1308,17 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1265
1308
|
// same detent). Without this, position goes stale mid-animation because
|
|
1266
1309
|
// the drawer's `transitionrun` never fires.
|
|
1267
1310
|
const onTransitionRun = (e: TransitionEvent) => {
|
|
1268
|
-
if (
|
|
1311
|
+
if (
|
|
1312
|
+
(e.target === drawer || e.target === wrapper) &&
|
|
1313
|
+
(e.propertyName === 'transform' || e.propertyName === '--snap-point-height')
|
|
1314
|
+
)
|
|
1269
1315
|
state.start();
|
|
1270
1316
|
};
|
|
1271
1317
|
const onTransitionDone = (e: TransitionEvent) => {
|
|
1272
|
-
if (
|
|
1318
|
+
if (
|
|
1319
|
+
(e.target === drawer || e.target === wrapper) &&
|
|
1320
|
+
(e.propertyName === 'transform' || e.propertyName === '--snap-point-height')
|
|
1321
|
+
)
|
|
1273
1322
|
state.stop();
|
|
1274
1323
|
};
|
|
1275
1324
|
const onAnimationStart = (e: AnimationEvent) => {
|
|
@@ -1376,6 +1425,9 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1376
1425
|
wasOpenRef.current = isOpen;
|
|
1377
1426
|
}, [isOpen, detached, drawerRef]);
|
|
1378
1427
|
|
|
1428
|
+
// Subsequent snap writes belong to useSnapPoints. A React style update on
|
|
1429
|
+
// release would overwrite the live drag value before its transition starts.
|
|
1430
|
+
const initialSnapPointOffset = React.useRef(snapPointsOffset?.[activeSnapPointIndex ?? 0] ?? 0);
|
|
1379
1431
|
const contentNode = (
|
|
1380
1432
|
<DialogPrimitive.Content
|
|
1381
1433
|
data-vaul-drawer-direction={direction}
|
|
@@ -1390,7 +1442,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1390
1442
|
style={
|
|
1391
1443
|
snapPointsOffset && snapPointsOffset.length > 0
|
|
1392
1444
|
? ({
|
|
1393
|
-
'--snap-point-height': `${
|
|
1445
|
+
'--snap-point-height': `${initialSnapPointOffset.current}px`,
|
|
1446
|
+
'transform': getSnapPointTransform(direction),
|
|
1394
1447
|
...style,
|
|
1395
1448
|
'pointerEvents': 'auto',
|
|
1396
1449
|
} as React.CSSProperties)
|
|
@@ -1453,6 +1506,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1453
1506
|
activeSnapPointIndex > 0
|
|
1454
1507
|
) {
|
|
1455
1508
|
setActiveSnapPoint(snapPoints![0]!);
|
|
1509
|
+
} else {
|
|
1510
|
+
onDismissAttempt?.();
|
|
1456
1511
|
}
|
|
1457
1512
|
}
|
|
1458
1513
|
}}
|
|
@@ -1594,6 +1649,7 @@ export const Handle = React.forwardRef<HTMLDivElement, HandleProps>(
|
|
|
1594
1649
|
activeSnapPoint,
|
|
1595
1650
|
setActiveSnapPoint,
|
|
1596
1651
|
dismissible,
|
|
1652
|
+
onDismissAttempt,
|
|
1597
1653
|
handleOnly,
|
|
1598
1654
|
isOpen,
|
|
1599
1655
|
onPress,
|
|
@@ -1637,6 +1693,12 @@ export const Handle = React.forwardRef<HTMLDivElement, HandleProps>(
|
|
|
1637
1693
|
return;
|
|
1638
1694
|
}
|
|
1639
1695
|
|
|
1696
|
+
// Mirrors native: a single-detent sheet's handle tap is a dismiss.
|
|
1697
|
+
if (isLastSnapPoint && snapPoints.length === 1) {
|
|
1698
|
+
onDismissAttempt?.();
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1640
1702
|
const currentSnapIndex = snapPoints.findIndex((point) => point === activeSnapPoint);
|
|
1641
1703
|
if (currentSnapIndex === -1) return; // activeSnapPoint not found in snapPoints
|
|
1642
1704
|
const nextSnapPoint = snapPoints[currentSnapIndex + 1];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck — vendored upstream, incompatible with noUncheckedIndexedAccess
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { set, isVertical } from './helpers';
|
|
3
|
+
import { set, isVertical, getSnapPointTransform } from './helpers';
|
|
4
4
|
import { DEFAULT_PEEK_HEIGHT, TRANSITIONS, VELOCITY_THRESHOLD } from './constants';
|
|
5
5
|
import { useControllableState } from './use-controllable-state';
|
|
6
6
|
import type { DrawerDirection } from './types';
|
|
@@ -198,16 +198,12 @@ export function useSnapPoints({
|
|
|
198
198
|
|
|
199
199
|
const animateThisSnap = hasSnappedRef.current || initialAnimated;
|
|
200
200
|
hasSnappedRef.current = true;
|
|
201
|
-
//
|
|
202
|
-
// @property) so layouts derived from it (e.g. the scrollable fill) resize
|
|
203
|
-
// in sync with the drawer's slide instead of jumping to the target.
|
|
201
|
+
// One animated value drives both the translation and the visible height.
|
|
204
202
|
set(drawerRef.current, {
|
|
205
203
|
'transition': animateThisSnap
|
|
206
|
-
?
|
|
204
|
+
? `--snap-point-height ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`
|
|
207
205
|
: 'none',
|
|
208
|
-
'transform':
|
|
209
|
-
? `translate3d(0, ${dimension}px, 0)`
|
|
210
|
-
: `translate3d(${dimension}px, 0, 0)`,
|
|
206
|
+
'transform': getSnapPointTransform(direction),
|
|
211
207
|
'--snap-point-height': `${dimension}px`,
|
|
212
208
|
});
|
|
213
209
|
|
|
@@ -364,9 +360,7 @@ export function useSnapPoints({
|
|
|
364
360
|
if ((direction === 'bottom' || direction === 'right') && newValue > snapPointsOffset[0]) {
|
|
365
361
|
const excess = newValue - snapPointsOffset[0];
|
|
366
362
|
set(drawerRef.current, {
|
|
367
|
-
'transform':
|
|
368
|
-
? `translate3d(0, ${snapPointsOffset[0]}px, 0)`
|
|
369
|
-
: `translate3d(${snapPointsOffset[0]}px, 0, 0)`,
|
|
363
|
+
'transform': getSnapPointTransform(direction),
|
|
370
364
|
'--snap-point-height': `${snapPointsOffset[0]}px`,
|
|
371
365
|
});
|
|
372
366
|
setDetachedWrapperTransform(excess, false);
|
|
@@ -378,9 +372,7 @@ export function useSnapPoints({
|
|
|
378
372
|
// derived from it (e.g. the scrollable fill) resize with the drawer
|
|
379
373
|
// instead of staying cut off at the last detent's visible height.
|
|
380
374
|
set(drawerRef.current, {
|
|
381
|
-
'transform':
|
|
382
|
-
? `translate3d(0, ${newValue}px, 0)`
|
|
383
|
-
: `translate3d(${newValue}px, 0, 0)`,
|
|
375
|
+
'transform': getSnapPointTransform(direction),
|
|
384
376
|
'--snap-point-height': `${newValue}px`,
|
|
385
377
|
});
|
|
386
378
|
}
|