@lodev09/react-native-true-sheet 3.11.7 → 3.11.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/TrueSheet.web.js +74 -36
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetPeek.web.js +15 -6
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/web/vaul/context.js +2 -0
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/index.js +58 -10
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetPeek.web.d.ts +9 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +8 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/TrueSheet.web.tsx +80 -40
- package/src/TrueSheetPeek.web.tsx +22 -7
- package/src/web/vaul/context.ts +14 -0
- package/src/web/vaul/index.tsx +71 -11
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useRef
|
|
2
|
-
import {
|
|
1
|
+
import { createContext, type RefObject, useContext, useEffect, useRef } from 'react';
|
|
2
|
+
import { type LayoutChangeEvent, View, type ViewProps } from 'react-native';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Reports the measured peek content height to the owning TrueSheet.
|
|
@@ -7,11 +7,25 @@ import { View, type LayoutChangeEvent, type ViewProps } from 'react-native';
|
|
|
7
7
|
*/
|
|
8
8
|
export interface TrueSheetPeekContextValue {
|
|
9
9
|
contentRef: RefObject<View | null>;
|
|
10
|
+
peekRef: RefObject<View | null>;
|
|
10
11
|
setPeekContentHeight: (height: number) => void;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const TrueSheetPeekContext = createContext<TrueSheetPeekContextValue | null>(null);
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Distance from the top of the content view to the bottom of the peek view,
|
|
18
|
+
* so the peek view's offset within the content (padding, views above it)
|
|
19
|
+
* counts toward the peek detent. Both rects live in the drawer's transformed
|
|
20
|
+
* subtree, so an in-flight translate cancels out of the delta.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export const measurePeekContentHeight = (
|
|
24
|
+
peekElement: HTMLElement,
|
|
25
|
+
contentElement: HTMLElement
|
|
26
|
+
): number =>
|
|
27
|
+
peekElement.getBoundingClientRect().bottom - contentElement.getBoundingClientRect().top;
|
|
28
|
+
|
|
15
29
|
/**
|
|
16
30
|
* Wrapper component that marks its children as the sheet's peek content.
|
|
17
31
|
* When rendered within a `TrueSheet`, the `"peek"` detent reveals everything
|
|
@@ -20,7 +34,11 @@ export const TrueSheetPeekContext = createContext<TrueSheetPeekContextValue | nu
|
|
|
20
34
|
*/
|
|
21
35
|
export const TrueSheetPeek = ({ onLayout, ...rest }: ViewProps) => {
|
|
22
36
|
const context = useContext(TrueSheetPeekContext);
|
|
23
|
-
const
|
|
37
|
+
const localRef = useRef<View>(null);
|
|
38
|
+
|
|
39
|
+
// Register into the sheet's peek ref so it can measure the peek live
|
|
40
|
+
// during detent geometry computation (state lags the first willPresent).
|
|
41
|
+
const viewRef = context?.peekRef ?? localRef;
|
|
24
42
|
|
|
25
43
|
useEffect(() => () => context?.setPeekContentHeight(0), [context]);
|
|
26
44
|
|
|
@@ -30,12 +48,9 @@ export const TrueSheetPeek = ({ onLayout, ...rest }: ViewProps) => {
|
|
|
30
48
|
const peekElement = viewRef.current as unknown as HTMLElement | null;
|
|
31
49
|
const contentElement = context.contentRef.current as unknown as HTMLElement | null;
|
|
32
50
|
|
|
33
|
-
// Distance from the top of the content view to the bottom of the peek view,
|
|
34
|
-
// so the peek view's offset within the content (padding, views above it)
|
|
35
|
-
// counts toward the peek detent.
|
|
36
51
|
const bottom =
|
|
37
52
|
peekElement && contentElement
|
|
38
|
-
? peekElement
|
|
53
|
+
? measurePeekContentHeight(peekElement, contentElement)
|
|
39
54
|
: event.nativeEvent.layout.height;
|
|
40
55
|
|
|
41
56
|
context.setPeekContentHeight(bottom);
|
package/src/web/vaul/context.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { DrawerDirection } from './types';
|
|
3
3
|
|
|
4
|
+
export interface DragEvent {
|
|
5
|
+
target: EventTarget;
|
|
6
|
+
pageX: number;
|
|
7
|
+
pageY: number;
|
|
8
|
+
pointerType: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
interface DrawerContextValue {
|
|
5
12
|
drawerRef: React.RefObject<HTMLDivElement | null>;
|
|
6
13
|
overlayRef: React.RefObject<HTMLDivElement | null>;
|
|
7
14
|
onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
8
15
|
onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
|
|
9
16
|
onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
17
|
+
onTouchDrag: (event: DragEvent, pointerEvent?: React.PointerEvent<HTMLDivElement>) => void;
|
|
18
|
+
onTouchRelease: (
|
|
19
|
+
event: DragEvent | null,
|
|
20
|
+
pointerEvent?: React.PointerEvent<HTMLDivElement>
|
|
21
|
+
) => void;
|
|
10
22
|
onNestedDrag: (event: React.PointerEvent<HTMLDivElement>, percentageDragged: number) => void;
|
|
11
23
|
onNestedOpenChange: (o: boolean) => void;
|
|
12
24
|
onNestedRelease: (event: React.PointerEvent<HTMLDivElement>, open: boolean) => void;
|
|
@@ -47,6 +59,8 @@ export const DrawerContext = React.createContext<DrawerContextValue>({
|
|
|
47
59
|
onPress: () => {},
|
|
48
60
|
onRelease: () => {},
|
|
49
61
|
onDrag: () => {},
|
|
62
|
+
onTouchDrag: () => {},
|
|
63
|
+
onTouchRelease: () => {},
|
|
50
64
|
onNestedDrag: () => {},
|
|
51
65
|
onNestedOpenChange: () => {},
|
|
52
66
|
onNestedRelease: () => {},
|
package/src/web/vaul/index.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import React from 'react';
|
|
|
5
5
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
6
6
|
import { Presence } from '@radix-ui/react-presence';
|
|
7
7
|
|
|
8
|
-
import { DrawerContext, useDrawerContext } from './context';
|
|
8
|
+
import { DrawerContext, useDrawerContext, type DragEvent } from './context';
|
|
9
9
|
import './style.css';
|
|
10
10
|
|
|
11
11
|
import { isIOS, isMobileFirefox } from './browser';
|
|
@@ -491,7 +491,7 @@ export function Root({
|
|
|
491
491
|
return true;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
function
|
|
494
|
+
function drag(event: DragEvent, pointerEvent?: React.PointerEvent<HTMLDivElement>) {
|
|
495
495
|
if (!drawerRef.current) {
|
|
496
496
|
return;
|
|
497
497
|
}
|
|
@@ -557,7 +557,7 @@ export function Root({
|
|
|
557
557
|
transition: 'none',
|
|
558
558
|
});
|
|
559
559
|
|
|
560
|
-
onDragProp?.(
|
|
560
|
+
if (pointerEvent) onDragProp?.(pointerEvent, percentageDragged);
|
|
561
561
|
|
|
562
562
|
if (snapPoints) {
|
|
563
563
|
onDragSnapPoints({ draggedDistance });
|
|
@@ -761,7 +761,7 @@ export function Root({
|
|
|
761
761
|
dragEndTime.current = new Date();
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
function
|
|
764
|
+
function release(event: DragEvent | null, pointerEvent?: React.PointerEvent<HTMLDivElement>) {
|
|
765
765
|
if (!isDragging || !drawerRef.current) return;
|
|
766
766
|
|
|
767
767
|
drawerRef.current.classList.remove(DRAG_CLASS);
|
|
@@ -797,20 +797,20 @@ export function Root({
|
|
|
797
797
|
velocity,
|
|
798
798
|
dismissible,
|
|
799
799
|
});
|
|
800
|
-
onReleaseProp?.(
|
|
800
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, true);
|
|
801
801
|
return;
|
|
802
802
|
}
|
|
803
803
|
|
|
804
804
|
// Moved upwards, don't do anything
|
|
805
805
|
if (direction === 'bottom' || direction === 'right' ? distMoved > 0 : distMoved < 0) {
|
|
806
806
|
resetDrawer();
|
|
807
|
-
onReleaseProp?.(
|
|
807
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, true);
|
|
808
808
|
return;
|
|
809
809
|
}
|
|
810
810
|
|
|
811
811
|
if (velocity > VELOCITY_THRESHOLD) {
|
|
812
812
|
closeDrawer();
|
|
813
|
-
onReleaseProp?.(
|
|
813
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, false);
|
|
814
814
|
return;
|
|
815
815
|
}
|
|
816
816
|
|
|
@@ -829,11 +829,11 @@ export function Root({
|
|
|
829
829
|
(isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold
|
|
830
830
|
) {
|
|
831
831
|
closeDrawer();
|
|
832
|
-
onReleaseProp?.(
|
|
832
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, false);
|
|
833
833
|
return;
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
-
onReleaseProp?.(
|
|
836
|
+
if (pointerEvent) onReleaseProp?.(pointerEvent, true);
|
|
837
837
|
resetDrawer();
|
|
838
838
|
}
|
|
839
839
|
|
|
@@ -943,8 +943,10 @@ export function Root({
|
|
|
943
943
|
overlayRef,
|
|
944
944
|
onOpenChange,
|
|
945
945
|
onPress,
|
|
946
|
-
onRelease,
|
|
947
|
-
onDrag,
|
|
946
|
+
onRelease: (event) => release(event, event ?? undefined),
|
|
947
|
+
onDrag: (event) => drag(event, event),
|
|
948
|
+
onTouchDrag: (event, pointerEvent) => drag(event, pointerEvent),
|
|
949
|
+
onTouchRelease: (event, pointerEvent) => release(event, pointerEvent),
|
|
948
950
|
dismissible,
|
|
949
951
|
shouldAnimate,
|
|
950
952
|
handleOnly,
|
|
@@ -1088,6 +1090,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1088
1090
|
onPress,
|
|
1089
1091
|
onRelease,
|
|
1090
1092
|
onDrag,
|
|
1093
|
+
onTouchDrag,
|
|
1094
|
+
onTouchRelease,
|
|
1091
1095
|
keyboardIsOpen,
|
|
1092
1096
|
snapPointsOffset,
|
|
1093
1097
|
activeSnapPointIndex,
|
|
@@ -1144,6 +1148,7 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1144
1148
|
const composedRef = useComposedRefs(ref, drawerRef);
|
|
1145
1149
|
const pointerStartRef = React.useRef<{ x: number; y: number } | null>(null);
|
|
1146
1150
|
const lastKnownPointerEventRef = React.useRef<React.PointerEvent<HTMLDivElement> | null>(null);
|
|
1151
|
+
const continuingCanceledTouchRef = React.useRef(false);
|
|
1147
1152
|
const wasBeyondThePointRef = React.useRef(false);
|
|
1148
1153
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
1149
1154
|
useScaleBackground();
|
|
@@ -1376,6 +1381,7 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1376
1381
|
onPointerDown={(event) => {
|
|
1377
1382
|
if (handleOnly) return;
|
|
1378
1383
|
rest.onPointerDown?.(event);
|
|
1384
|
+
continuingCanceledTouchRef.current = false;
|
|
1379
1385
|
pointerStartRef.current = { x: event.pageX, y: event.pageY };
|
|
1380
1386
|
onPress(event);
|
|
1381
1387
|
}}
|
|
@@ -1462,14 +1468,68 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(
|
|
|
1462
1468
|
}}
|
|
1463
1469
|
onPointerUp={(event) => {
|
|
1464
1470
|
rest.onPointerUp?.(event);
|
|
1471
|
+
continuingCanceledTouchRef.current = false;
|
|
1465
1472
|
pointerStartRef.current = null;
|
|
1466
1473
|
wasBeyondThePointRef.current = false;
|
|
1467
1474
|
onRelease(event);
|
|
1468
1475
|
}}
|
|
1476
|
+
onPointerCancel={(event) => {
|
|
1477
|
+
rest.onPointerCancel?.(event);
|
|
1478
|
+
if (
|
|
1479
|
+
event.pointerType === 'touch' &&
|
|
1480
|
+
snapPoints &&
|
|
1481
|
+
activeSnapPointIndex === snapPoints.length - 1
|
|
1482
|
+
) {
|
|
1483
|
+
continuingCanceledTouchRef.current = true;
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
handleOnPointerUp(null);
|
|
1487
|
+
}}
|
|
1469
1488
|
onPointerOut={(event) => {
|
|
1470
1489
|
rest.onPointerOut?.(event);
|
|
1490
|
+
if (continuingCanceledTouchRef.current) return;
|
|
1471
1491
|
handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1472
1492
|
}}
|
|
1493
|
+
onTouchMove={(event) => {
|
|
1494
|
+
rest.onTouchMove?.(event);
|
|
1495
|
+
if (!continuingCanceledTouchRef.current) return;
|
|
1496
|
+
const touch = event.touches[0];
|
|
1497
|
+
if (!touch) return;
|
|
1498
|
+
onTouchDrag(
|
|
1499
|
+
{
|
|
1500
|
+
target: event.target,
|
|
1501
|
+
pageX: touch.pageX,
|
|
1502
|
+
pageY: touch.pageY,
|
|
1503
|
+
pointerType: 'touch',
|
|
1504
|
+
},
|
|
1505
|
+
lastKnownPointerEventRef.current ?? undefined
|
|
1506
|
+
);
|
|
1507
|
+
}}
|
|
1508
|
+
onTouchEnd={(event) => {
|
|
1509
|
+
rest.onTouchEnd?.(event);
|
|
1510
|
+
if (!continuingCanceledTouchRef.current) return;
|
|
1511
|
+
continuingCanceledTouchRef.current = false;
|
|
1512
|
+
pointerStartRef.current = null;
|
|
1513
|
+
wasBeyondThePointRef.current = false;
|
|
1514
|
+
const touch = event.changedTouches[0];
|
|
1515
|
+
onTouchRelease(
|
|
1516
|
+
touch
|
|
1517
|
+
? {
|
|
1518
|
+
target: event.target,
|
|
1519
|
+
pageX: touch.pageX,
|
|
1520
|
+
pageY: touch.pageY,
|
|
1521
|
+
pointerType: 'touch',
|
|
1522
|
+
}
|
|
1523
|
+
: null,
|
|
1524
|
+
lastKnownPointerEventRef.current ?? undefined
|
|
1525
|
+
);
|
|
1526
|
+
}}
|
|
1527
|
+
onTouchCancel={(event) => {
|
|
1528
|
+
rest.onTouchCancel?.(event);
|
|
1529
|
+
if (!continuingCanceledTouchRef.current) return;
|
|
1530
|
+
continuingCanceledTouchRef.current = false;
|
|
1531
|
+
handleOnPointerUp(null);
|
|
1532
|
+
}}
|
|
1473
1533
|
onContextMenu={(event) => {
|
|
1474
1534
|
rest.onContextMenu?.(event);
|
|
1475
1535
|
if (lastKnownPointerEventRef.current) {
|