@lodev09/react-native-true-sheet 3.11.0-beta.1 → 3.11.0-beta.11
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/LICENSE +1 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +19 -2
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +3 -3
- package/ios/TrueSheetFooterView.mm +10 -1
- package/ios/TrueSheetView.mm +1 -1
- package/ios/TrueSheetViewController.h +1 -1
- package/ios/TrueSheetViewController.mm +10 -4
- package/lib/module/TrueSheet.js +2 -2
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +222 -50
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetProvider.web.js +5 -2
- package/lib/module/TrueSheetProvider.web.js.map +1 -1
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +1 -1
- package/lib/module/navigation/TrueSheetRouter.js +1 -1
- package/lib/module/navigation/TrueSheetRouter.js.map +1 -1
- package/lib/module/navigation/createTrueSheetNavigator.js +1 -1
- package/lib/module/navigation/createTrueSheetNavigator.js.map +1 -1
- package/lib/module/navigation/useTrueSheetNavigation.js +1 -1
- package/lib/module/navigation/useTrueSheetNavigation.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/vaul/browser.js +1 -1
- package/lib/module/web/vaul/browser.js.map +1 -1
- package/lib/module/web/vaul/index.js +88 -133
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/style.css +1 -1
- package/lib/module/web/vaul/style.css.d.js +2 -0
- package/lib/module/web/vaul/style.css.d.js.map +1 -0
- package/lib/typescript/src/TrueSheet.types.d.ts +6 -4
- 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/TrueSheetProvider.web.d.ts +2 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +1 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +1 -1
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetView.d.ts +1 -1
- package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +1 -1
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +1 -1
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/types.d.ts +1 -1
- package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +2 -2
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.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/vaul/index.d.ts +7 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/package.json +26 -19
- package/src/TrueSheet.tsx +2 -2
- package/src/TrueSheet.types.ts +6 -4
- package/src/TrueSheet.web.tsx +220 -52
- package/src/TrueSheetProvider.web.tsx +11 -2
- package/src/fabric/TrueSheetViewNativeComponent.ts +1 -1
- package/src/navigation/TrueSheetRouter.ts +1 -1
- package/src/navigation/TrueSheetView.tsx +1 -1
- package/src/navigation/createTrueSheetNavigator.tsx +1 -1
- package/src/navigation/screen/types.ts +1 -1
- package/src/navigation/screen/useSheetScreenState.ts +1 -1
- package/src/navigation/types.ts +2 -2
- package/src/navigation/useTrueSheetNavigation.ts +1 -1
- package/src/web/constants.ts +1 -1
- package/src/web/vaul/index.tsx +118 -146
- package/src/web/vaul/style.css +1 -1
- package/src/web/vaul/style.css.d.ts +1 -0
package/src/navigation/types.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
ParamListBase,
|
|
8
8
|
RouteProp,
|
|
9
9
|
StackActionHelpers,
|
|
10
|
-
} from '@react-navigation/
|
|
10
|
+
} from '@react-navigation/core';
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
13
|
DetentInfoEventPayload,
|
|
@@ -136,7 +136,7 @@ export type TrueSheetNavigationSheetProps = Pick<
|
|
|
136
136
|
| 'maxContentHeight'
|
|
137
137
|
| 'maxContentWidth'
|
|
138
138
|
| 'scrollable'
|
|
139
|
-
| '
|
|
139
|
+
| 'presentation'
|
|
140
140
|
| 'header'
|
|
141
141
|
| 'headerStyle'
|
|
142
142
|
| 'footer'
|
package/src/web/constants.ts
CHANGED
|
@@ -10,4 +10,4 @@ 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)';
|
|
11
11
|
export const DEFAULT_GRABBER_WIDTH = 32;
|
|
12
12
|
export const DEFAULT_GRABBER_HEIGHT = 4;
|
|
13
|
-
export const DEFAULT_GRABBER_TOP_MARGIN =
|
|
13
|
+
export const DEFAULT_GRABBER_TOP_MARGIN = 8;
|
package/src/web/vaul/index.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
|
|
5
5
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
6
|
+
import { Presence } from '@radix-ui/react-presence';
|
|
6
7
|
|
|
7
8
|
import { DrawerContext, useDrawerContext } from './context';
|
|
8
9
|
import './style.css';
|
|
@@ -185,6 +186,12 @@ export type DialogProps = {
|
|
|
185
186
|
* the sheet appears at its target detent instantly. Defaults to `true`.
|
|
186
187
|
*/
|
|
187
188
|
initialAnimated?: boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Fires when the auto-size wrapper's measured content height changes.
|
|
191
|
+
* Useful for callers that want to size the surrounding card to fit
|
|
192
|
+
* content (e.g. iPad-style form sheets).
|
|
193
|
+
*/
|
|
194
|
+
onContentHeightChange?: (height: number) => void;
|
|
188
195
|
} & (WithFadeFromProps | WithoutFadeFromProps);
|
|
189
196
|
|
|
190
197
|
export function Root({
|
|
@@ -225,6 +232,7 @@ export function Root({
|
|
|
225
232
|
detachedWrapperStyle,
|
|
226
233
|
maxContentHeight,
|
|
227
234
|
initialAnimated = true,
|
|
235
|
+
onContentHeightChange,
|
|
228
236
|
}: DialogProps) {
|
|
229
237
|
const [isOpen = false, setIsOpen] = useControllableState({
|
|
230
238
|
defaultProp: defaultOpen,
|
|
@@ -239,19 +247,6 @@ export function Root({
|
|
|
239
247
|
setTimeout(() => {
|
|
240
248
|
onAnimationEnd?.(o);
|
|
241
249
|
}, TRANSITIONS.DURATION * 1000);
|
|
242
|
-
|
|
243
|
-
if (o && !modal) {
|
|
244
|
-
if (typeof window !== 'undefined') {
|
|
245
|
-
window.requestAnimationFrame(() => {
|
|
246
|
-
document.body.style.pointerEvents = 'auto';
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (!o) {
|
|
252
|
-
// This will be removed when the exit animation ends (`500ms`)
|
|
253
|
-
document.body.style.pointerEvents = 'auto';
|
|
254
|
-
}
|
|
255
250
|
},
|
|
256
251
|
});
|
|
257
252
|
const [hasBeenOpened, setHasBeenOpened] = React.useState<boolean>(false);
|
|
@@ -274,6 +269,14 @@ export function Root({
|
|
|
274
269
|
const initialDrawerHeight = React.useRef(0);
|
|
275
270
|
const [contentHeight, setContentHeight] = React.useState(0);
|
|
276
271
|
|
|
272
|
+
const onContentHeightChangeRef = React.useRef(onContentHeightChange);
|
|
273
|
+
React.useEffect(() => {
|
|
274
|
+
onContentHeightChangeRef.current = onContentHeightChange;
|
|
275
|
+
});
|
|
276
|
+
React.useEffect(() => {
|
|
277
|
+
onContentHeightChangeRef.current?.(contentHeight);
|
|
278
|
+
}, [contentHeight]);
|
|
279
|
+
|
|
277
280
|
const onSnapPointChange = React.useCallback((activeSnapPointIndex: number) => {
|
|
278
281
|
// Change openTime ref when we reach the last snap point to prevent dragging for 500ms incase it's scrollable.
|
|
279
282
|
if (snapPoints && activeSnapPointIndex === snapPointsOffset.length - 1)
|
|
@@ -566,88 +569,6 @@ export function Root({
|
|
|
566
569
|
onPositionChangeRef.current = onPositionChange;
|
|
567
570
|
});
|
|
568
571
|
|
|
569
|
-
// Radix's DismissableLayer (modal) sets body pointer-events to "none" so the
|
|
570
|
-
// overlay traps clicks. When the active snap is below fadeFromIndex the
|
|
571
|
-
// overlay is fully transparent, and we want clicks to reach the content
|
|
572
|
-
// behind the drawer — so we restore body interactivity. DismissableLayer
|
|
573
|
-
// captures its DOM node via a ref-callback `setNode`, so its body write
|
|
574
|
-
// runs one render later than ours on initial open — re-assert on rAF so
|
|
575
|
-
// the final write is ours.
|
|
576
|
-
React.useEffect(() => {
|
|
577
|
-
if (!modal || !snapPoints || fadeFromIndex === undefined) return;
|
|
578
|
-
// On close, restore body interactivity. `useControllableState.onChange`
|
|
579
|
-
// only fires for internal `setIsOpen` calls — when the parent flips the
|
|
580
|
-
// `open` prop imperatively, the reset at line ~201 never runs.
|
|
581
|
-
if (!isOpen) {
|
|
582
|
-
document.body.style.pointerEvents = 'auto';
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
if (typeof activeSnapPointIndex !== 'number') return;
|
|
586
|
-
const isBelowFade = activeSnapPointIndex < fadeFromIndex;
|
|
587
|
-
const value = isBelowFade ? 'auto' : 'none';
|
|
588
|
-
document.body.style.pointerEvents = value;
|
|
589
|
-
const id = window.requestAnimationFrame(() => {
|
|
590
|
-
document.body.style.pointerEvents = value;
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
// React Navigation keeps unfocused screens mounted with `display: none` on
|
|
594
|
-
// web, so the drawer never unmounts and neither `isOpen` nor this effect's
|
|
595
|
-
// deps change — a lingering `none` body style would then leak to whichever
|
|
596
|
-
// screen becomes visible. Observe the drawer: when an ancestor hides it,
|
|
597
|
-
// restore `auto`; when it returns, re-apply the desired value.
|
|
598
|
-
let observer: IntersectionObserver | null = null;
|
|
599
|
-
let rafId = 0;
|
|
600
|
-
const startObserving = () => {
|
|
601
|
-
const node = drawerRef.current;
|
|
602
|
-
if (!node) {
|
|
603
|
-
rafId = window.requestAnimationFrame(startObserving);
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
observer = new IntersectionObserver((entries) => {
|
|
607
|
-
const entry = entries[entries.length - 1];
|
|
608
|
-
if (!entry) return;
|
|
609
|
-
document.body.style.pointerEvents = entry.isIntersecting ? value : 'auto';
|
|
610
|
-
});
|
|
611
|
-
observer.observe(node);
|
|
612
|
-
};
|
|
613
|
-
rafId = window.requestAnimationFrame(startObserving);
|
|
614
|
-
|
|
615
|
-
// Always restore on cleanup so an unmount while the sheet is still open
|
|
616
|
-
// doesn't leave the page uninteractive. If the effect re-runs for a
|
|
617
|
-
// normal state change, it re-applies the correct value.
|
|
618
|
-
return () => {
|
|
619
|
-
window.cancelAnimationFrame(id);
|
|
620
|
-
window.cancelAnimationFrame(rafId);
|
|
621
|
-
observer?.disconnect();
|
|
622
|
-
document.body.style.pointerEvents = 'auto';
|
|
623
|
-
};
|
|
624
|
-
}, [isOpen, modal, snapPoints, fadeFromIndex, activeSnapPointIndex]);
|
|
625
|
-
|
|
626
|
-
// Radix DismissableLayer's body-restore on unmount is broken: its two
|
|
627
|
-
// useEffects (see @radix-ui/react-dismissable-layer dist/index.mjs:68-92)
|
|
628
|
-
// run cleanups in reverse declaration order, so the layers-set decrement
|
|
629
|
-
// (effect B) runs BEFORE the size-1 body-restore check (effect A). When
|
|
630
|
-
// the last layer unmounts, A sees size=0 and skips the restore — body
|
|
631
|
-
// stays 'none'. Take ownership of the restore here on Drawer.Root unmount.
|
|
632
|
-
// Skipped when nested or non-modal so an inner drawer's unmount doesn't
|
|
633
|
-
// unlock an outer drawer's modal trap. Also bail if any other Radix
|
|
634
|
-
// dismissable layer is still open (sibling Dialog/AlertDialog/Popover) —
|
|
635
|
-
// that one wants the lock kept.
|
|
636
|
-
React.useEffect(() => {
|
|
637
|
-
if (nested || !modal) return;
|
|
638
|
-
return () => {
|
|
639
|
-
if (typeof document === 'undefined') return;
|
|
640
|
-
if (document.body.style.pointerEvents !== 'none') return;
|
|
641
|
-
if (
|
|
642
|
-
document.querySelector(
|
|
643
|
-
'[role="dialog"][data-state="open"], [role="alertdialog"][data-state="open"]'
|
|
644
|
-
)
|
|
645
|
-
)
|
|
646
|
-
return;
|
|
647
|
-
document.body.style.pointerEvents = 'auto';
|
|
648
|
-
};
|
|
649
|
-
}, [nested, modal]);
|
|
650
|
-
|
|
651
572
|
React.useEffect(() => {
|
|
652
573
|
function onVisualViewportChange() {
|
|
653
574
|
if (!drawerRef.current || !repositionInputs) return;
|
|
@@ -916,15 +837,6 @@ export function Root({
|
|
|
916
837
|
}
|
|
917
838
|
}
|
|
918
839
|
|
|
919
|
-
React.useEffect(() => {
|
|
920
|
-
if (!modal) {
|
|
921
|
-
// Need to do this manually unfortunately
|
|
922
|
-
window.requestAnimationFrame(() => {
|
|
923
|
-
document.body.style.pointerEvents = 'auto';
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
}, [modal]);
|
|
927
|
-
|
|
928
840
|
return (
|
|
929
841
|
<DialogPrimitive.Root
|
|
930
842
|
defaultOpen={defaultOpen}
|
|
@@ -939,7 +851,14 @@ export function Root({
|
|
|
939
851
|
setIsOpen(open);
|
|
940
852
|
}}
|
|
941
853
|
open={isOpen}
|
|
942
|
-
modal
|
|
854
|
+
// Always non-modal at the Radix level — Radix's modal mode locks
|
|
855
|
+
// body.style.pointerEvents and its restore on layer unmount is buggy
|
|
856
|
+
// (see git history). Vaul's own overlay handles click-trapping; the
|
|
857
|
+
// overlay sets pointer-events: none below fadeFromIndex so clicks
|
|
858
|
+
// pass through to the page (matches native dimmedDetentIndex). Vaul's
|
|
859
|
+
// own `modal` prop still drives overlay rendering and click-outside
|
|
860
|
+
// behavior below.
|
|
861
|
+
modal={false}
|
|
943
862
|
>
|
|
944
863
|
<DrawerContext.Provider
|
|
945
864
|
value={{
|
|
@@ -1035,26 +954,38 @@ export const Overlay = React.forwardRef<
|
|
|
1035
954
|
typeof activeSnapPointIndex === 'number' &&
|
|
1036
955
|
activeSnapPointIndex < fadeFromIndex;
|
|
1037
956
|
|
|
957
|
+
// Render our own overlay element instead of `DialogPrimitive.Overlay`:
|
|
958
|
+
// Radix's Overlay returns null when the Dialog is non-modal, and we run
|
|
959
|
+
// Radix as `modal={false}` to keep it from locking body pointer-events.
|
|
960
|
+
// `Presence` keeps the node mounted through the closing animation
|
|
961
|
+
// (`fadeOut`) before unmounting.
|
|
1038
962
|
return (
|
|
1039
|
-
<
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
963
|
+
<Presence present={isOpen}>
|
|
964
|
+
<div
|
|
965
|
+
onMouseUp={onMouseUp}
|
|
966
|
+
ref={composedRef}
|
|
967
|
+
data-state={isOpen ? 'open' : 'closed'}
|
|
968
|
+
data-vaul-overlay=""
|
|
969
|
+
data-vaul-snap-points={isOpen && hasSnapPoints ? 'true' : 'false'}
|
|
970
|
+
data-vaul-delayed-snap-points={delayedSnapPoints ? 'true' : 'false'}
|
|
971
|
+
data-vaul-snap-points-overlay={isOpen && shouldFade ? 'true' : 'false'}
|
|
972
|
+
data-vaul-animate={shouldAnimate?.current ? 'true' : 'false'}
|
|
973
|
+
{...rest}
|
|
974
|
+
style={overlayStyle(style, !!isBelowFade)}
|
|
975
|
+
/>
|
|
976
|
+
</Presence>
|
|
1050
977
|
);
|
|
1051
978
|
});
|
|
1052
979
|
|
|
1053
980
|
Overlay.displayName = 'Drawer.Overlay';
|
|
1054
981
|
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
982
|
+
const overlayStyle = (
|
|
983
|
+
style: React.CSSProperties | undefined,
|
|
984
|
+
isBelowFade: boolean
|
|
985
|
+
): React.CSSProperties => ({
|
|
986
|
+
...style,
|
|
987
|
+
pointerEvents: isBelowFade ? 'none' : 'auto',
|
|
988
|
+
});
|
|
1058
989
|
|
|
1059
990
|
export type ContentProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
|
1060
991
|
/**
|
|
@@ -1066,7 +997,18 @@ export type ContentProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive
|
|
|
1066
997
|
};
|
|
1067
998
|
|
|
1068
999
|
export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
1069
|
-
(
|
|
1000
|
+
(
|
|
1001
|
+
{
|
|
1002
|
+
onPointerDownOutside,
|
|
1003
|
+
onEscapeKeyDown,
|
|
1004
|
+
style,
|
|
1005
|
+
onOpenAutoFocus,
|
|
1006
|
+
children,
|
|
1007
|
+
detachedSiblings,
|
|
1008
|
+
...rest
|
|
1009
|
+
},
|
|
1010
|
+
ref
|
|
1011
|
+
) => {
|
|
1070
1012
|
const {
|
|
1071
1013
|
drawerRef,
|
|
1072
1014
|
onPress,
|
|
@@ -1081,6 +1023,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1081
1023
|
isDragging,
|
|
1082
1024
|
direction,
|
|
1083
1025
|
snapPoints,
|
|
1026
|
+
setActiveSnapPoint,
|
|
1027
|
+
dismissible,
|
|
1084
1028
|
container,
|
|
1085
1029
|
handleOnly,
|
|
1086
1030
|
shouldAnimate,
|
|
@@ -1092,20 +1036,17 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1092
1036
|
detachedRadius,
|
|
1093
1037
|
detachedWrapperStyle: detachedWrapperStyleProp,
|
|
1094
1038
|
} = useDrawerContext();
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
)
|
|
1099
|
-
|
|
1100
|
-
//
|
|
1101
|
-
// The drawer itself may be styled to a fixed viewport height, so we measure
|
|
1102
|
-
// an inner wrapper instead. Ref callback starts the observer as soon as the
|
|
1103
|
-
// node mounts (Radix Presence defers the portal mount past useEffect).
|
|
1039
|
+
// Always measure the inner wrapper's natural height. The drawer itself may
|
|
1040
|
+
// be styled to a fixed viewport height, so we measure an inner wrapper
|
|
1041
|
+
// instead. Ref callback starts the observer as soon as the node mounts
|
|
1042
|
+
// (Radix Presence defers the portal mount past useEffect). The measurement
|
|
1043
|
+
// drives the 'auto' snap point and is also exposed via
|
|
1044
|
+
// `onContentHeightChange` for callers that size the surrounding card.
|
|
1104
1045
|
const autoRoRef = React.useRef<ResizeObserver | null>(null);
|
|
1105
1046
|
const setAutoSizeNode = React.useCallback(
|
|
1106
1047
|
(node: HTMLDivElement | null) => {
|
|
1107
1048
|
autoRoRef.current?.disconnect();
|
|
1108
|
-
if (!node
|
|
1049
|
+
if (!node) {
|
|
1109
1050
|
autoRoRef.current = null;
|
|
1110
1051
|
return;
|
|
1111
1052
|
}
|
|
@@ -1115,7 +1056,7 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1115
1056
|
ro.observe(node);
|
|
1116
1057
|
autoRoRef.current = ro;
|
|
1117
1058
|
},
|
|
1118
|
-
[
|
|
1059
|
+
[setContentHeight]
|
|
1119
1060
|
);
|
|
1120
1061
|
|
|
1121
1062
|
const isBelowFade =
|
|
@@ -1310,7 +1251,10 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1310
1251
|
// Translate the wrapper off-screen on dismiss so the whole card slides out
|
|
1311
1252
|
// as one. The reset-then-target pattern on open forces the browser to
|
|
1312
1253
|
// record a starting value so the transition actually animates.
|
|
1313
|
-
|
|
1254
|
+
// Start at `false` so a mount with `isOpen=true` (autopresent via
|
|
1255
|
+
// `initialDetentIndex`) is detected as a false→true transition and runs
|
|
1256
|
+
// the slide-up animation instead of snapping straight to rest.
|
|
1257
|
+
const wasOpenRef = React.useRef(false);
|
|
1314
1258
|
React.useEffect(() => {
|
|
1315
1259
|
if (!drawerRef.current) {
|
|
1316
1260
|
wasOpenRef.current = isOpen;
|
|
@@ -1330,10 +1274,6 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1330
1274
|
void wrapper.offsetHeight;
|
|
1331
1275
|
wrapper.style.transition = transition;
|
|
1332
1276
|
wrapper.style.transform = 'translate3d(0, 0, 0)';
|
|
1333
|
-
} else if (isOpen && !wrapper.style.transform) {
|
|
1334
|
-
// Fresh mount with open=true — ensure the wrapper starts at rest.
|
|
1335
|
-
wrapper.style.transition = transition;
|
|
1336
|
-
wrapper.style.transform = 'translate3d(0, 0, 0)';
|
|
1337
1277
|
}
|
|
1338
1278
|
}
|
|
1339
1279
|
wasOpenRef.current = isOpen;
|
|
@@ -1380,16 +1320,52 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1380
1320
|
return;
|
|
1381
1321
|
}
|
|
1382
1322
|
|
|
1323
|
+
// Non-dismissible + dimmed: collapse to the highest non-dimmed
|
|
1324
|
+
// snap point instead of silently swallowing the dismiss attempt.
|
|
1325
|
+
// Mirrors the native Android dim-tap behavior.
|
|
1326
|
+
if (
|
|
1327
|
+
!dismissible &&
|
|
1328
|
+
hasSnapPoints &&
|
|
1329
|
+
fadeFromIndex !== undefined &&
|
|
1330
|
+
fadeFromIndex > 0 &&
|
|
1331
|
+
typeof activeSnapPointIndex === 'number' &&
|
|
1332
|
+
activeSnapPointIndex >= fadeFromIndex
|
|
1333
|
+
) {
|
|
1334
|
+
e.preventDefault();
|
|
1335
|
+
setActiveSnapPoint(snapPoints![fadeFromIndex - 1]!);
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1383
1339
|
if (keyboardIsOpen.current) {
|
|
1384
1340
|
keyboardIsOpen.current = false;
|
|
1385
1341
|
}
|
|
1386
1342
|
}}
|
|
1387
|
-
|
|
1388
|
-
|
|
1343
|
+
onEscapeKeyDown={(e) => {
|
|
1344
|
+
onEscapeKeyDown?.(e);
|
|
1345
|
+
if (e.defaultPrevented) return;
|
|
1346
|
+
|
|
1347
|
+
// Non-dismissible: collapse to the first snap point (if above it)
|
|
1348
|
+
// instead of silently swallowing the dismiss attempt. Mirrors the
|
|
1349
|
+
// native Android back-press behavior.
|
|
1350
|
+
if (!dismissible) {
|
|
1389
1351
|
e.preventDefault();
|
|
1390
|
-
|
|
1352
|
+
if (
|
|
1353
|
+
hasSnapPoints &&
|
|
1354
|
+
typeof activeSnapPointIndex === 'number' &&
|
|
1355
|
+
activeSnapPointIndex > 0
|
|
1356
|
+
) {
|
|
1357
|
+
setActiveSnapPoint(snapPoints![0]!);
|
|
1358
|
+
}
|
|
1391
1359
|
}
|
|
1392
1360
|
}}
|
|
1361
|
+
onFocusOutside={(e) => {
|
|
1362
|
+
// Never auto-dismiss the sheet on focus shifts. Without Radix's
|
|
1363
|
+
// FocusScope trap (we run modal={false}), this fires whenever
|
|
1364
|
+
// focus naturally leaves the drawer — e.g. React Navigation's
|
|
1365
|
+
// web driver hides the previous screen via display:none, which
|
|
1366
|
+
// moves focus off the trigger and dismisses the sheet.
|
|
1367
|
+
e.preventDefault();
|
|
1368
|
+
}}
|
|
1393
1369
|
onPointerMove={(event) => {
|
|
1394
1370
|
lastKnownPointerEventRef.current = event;
|
|
1395
1371
|
if (handleOnly) return;
|
|
@@ -1427,13 +1403,9 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1427
1403
|
}
|
|
1428
1404
|
}}
|
|
1429
1405
|
>
|
|
1430
|
-
{
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
</div>
|
|
1434
|
-
) : (
|
|
1435
|
-
children
|
|
1436
|
-
)}
|
|
1406
|
+
<div ref={setAutoSizeNode} data-vaul-auto-size-wrapper="" style={autoSizeWrapperStyle}>
|
|
1407
|
+
{children}
|
|
1408
|
+
</div>
|
|
1437
1409
|
</DialogPrimitive.Content>
|
|
1438
1410
|
);
|
|
1439
1411
|
|
package/src/web/vaul/style.css
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module './style.css';
|