react-modal-sheet 2.2.1 → 3.1.0
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 +60 -44
- package/dist/index.d.mts +64 -0
- package/dist/index.d.ts +64 -8
- package/dist/index.js +838 -5
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +835 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -36
- package/src/SheetBackdrop.tsx +5 -3
- package/src/SheetContainer.tsx +5 -3
- package/src/SheetContent.tsx +5 -3
- package/src/SheetHeader.tsx +7 -5
- package/src/SheetScroller.tsx +7 -5
- package/src/context.tsx +8 -10
- package/src/hooks.tsx +44 -28
- package/src/index.tsx +7 -7
- package/src/sheet.tsx +66 -40
- package/src/styles.ts +2 -2
- package/src/types.tsx +45 -35
- package/src/use-prevent-scroll.ts +26 -23
- package/src/utils.ts +6 -5
- package/dist/SheetBackdrop.d.ts +0 -5
- package/dist/SheetContainer.d.ts +0 -7
- package/dist/SheetContent.d.ts +0 -8
- package/dist/SheetHeader.d.ts +0 -8
- package/dist/SheetScroller.d.ts +0 -5
- package/dist/constants.d.ts +0 -12
- package/dist/context.d.ts +0 -9
- package/dist/hooks.d.ts +0 -15
- package/dist/react-modal-sheet.cjs.development.js +0 -940
- package/dist/react-modal-sheet.cjs.development.js.map +0 -1
- package/dist/react-modal-sheet.cjs.production.min.js +0 -2
- package/dist/react-modal-sheet.cjs.production.min.js.map +0 -1
- package/dist/react-modal-sheet.esm.js +0 -936
- package/dist/react-modal-sheet.esm.js.map +0 -1
- package/dist/sheet.d.ts +0 -279
- package/dist/styles.d.ts +0 -5
- package/dist/types.d.ts +0 -84
- package/dist/use-prevent-scroll.d.ts +0 -11
- package/dist/utils.d.ts +0 -11
package/src/sheet.tsx
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import React, {
|
|
2
|
+
Children,
|
|
3
|
+
cloneElement,
|
|
4
|
+
forwardRef,
|
|
5
|
+
useEffect,
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
} from 'react';
|
|
10
|
+
|
|
11
|
+
import { createPortal } from 'react-dom';
|
|
3
12
|
|
|
4
13
|
import {
|
|
5
14
|
animate,
|
|
6
15
|
AnimatePresence,
|
|
7
16
|
motion,
|
|
8
|
-
PanInfo,
|
|
9
|
-
Transition,
|
|
17
|
+
type PanInfo,
|
|
18
|
+
type Transition,
|
|
10
19
|
useMotionValue,
|
|
11
20
|
useReducedMotion,
|
|
12
21
|
useTransform,
|
|
@@ -14,9 +23,9 @@ import {
|
|
|
14
23
|
|
|
15
24
|
import {
|
|
16
25
|
useModalEffect,
|
|
17
|
-
|
|
26
|
+
useDimensions,
|
|
18
27
|
useIsomorphicLayoutEffect,
|
|
19
|
-
|
|
28
|
+
useEffectEvent,
|
|
20
29
|
} from './hooks';
|
|
21
30
|
|
|
22
31
|
import {
|
|
@@ -27,13 +36,13 @@ import {
|
|
|
27
36
|
IS_SSR,
|
|
28
37
|
} from './constants';
|
|
29
38
|
|
|
30
|
-
import { SheetContextType, SheetProps } from './types';
|
|
39
|
+
import { type SheetContextType, type SheetProps } from './types';
|
|
31
40
|
import { SheetScrollerContextProvider, SheetContext } from './context';
|
|
32
41
|
import { getClosest, inDescendingOrder, validateSnapTo } from './utils';
|
|
33
42
|
import { usePreventScroll } from './use-prevent-scroll';
|
|
34
43
|
import styles from './styles';
|
|
35
44
|
|
|
36
|
-
const Sheet =
|
|
45
|
+
const Sheet = forwardRef<any, SheetProps>(
|
|
37
46
|
(
|
|
38
47
|
{
|
|
39
48
|
onOpenStart,
|
|
@@ -58,9 +67,9 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
58
67
|
},
|
|
59
68
|
ref
|
|
60
69
|
) => {
|
|
61
|
-
const sheetRef =
|
|
70
|
+
const sheetRef = useRef<HTMLDivElement>(null);
|
|
62
71
|
const indicatorRotation = useMotionValue(0);
|
|
63
|
-
const windowHeight =
|
|
72
|
+
const { height: windowHeight } = useDimensions();
|
|
64
73
|
const shouldReduceMotion = useReducedMotion();
|
|
65
74
|
const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
|
|
66
75
|
const animationOptions: Transition = {
|
|
@@ -73,17 +82,17 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
73
82
|
// and after that it is driven by the gestures and/or snapping
|
|
74
83
|
const y = useMotionValue(0);
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
// +2 for tolerance in case the animated value is slightly off
|
|
86
|
+
const zIndex = useTransform(y, (value) =>
|
|
87
|
+
value + 2 >= windowHeight ? -1 : 9999999
|
|
78
88
|
);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
value >= windowHeight ? 'hidden' : 'visible'
|
|
89
|
+
const visibility = useTransform(y, (value) =>
|
|
90
|
+
value + 2 >= windowHeight ? 'hidden' : 'visible'
|
|
82
91
|
);
|
|
83
92
|
|
|
84
93
|
// Keep the callback fns up-to-date so that they can be accessed inside
|
|
85
94
|
// the effect without including them to the dependencies array
|
|
86
|
-
const callbacks =
|
|
95
|
+
const callbacks = useRef({
|
|
87
96
|
onOpenStart,
|
|
88
97
|
onOpenEnd,
|
|
89
98
|
onCloseStart,
|
|
@@ -101,7 +110,7 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
101
110
|
|
|
102
111
|
if (snapPoints) {
|
|
103
112
|
// Convert negative / percentage snap points to absolute values
|
|
104
|
-
snapPoints = snapPoints.map(point => {
|
|
113
|
+
snapPoints = snapPoints.map((point) => {
|
|
105
114
|
// Percentage values e.g. between 0.0 and 1.0
|
|
106
115
|
if (point > 0 && point <= 1) return Math.round(point * windowHeight);
|
|
107
116
|
return point < 0 ? windowHeight + point : point; // negative values
|
|
@@ -109,11 +118,13 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
109
118
|
|
|
110
119
|
console.assert(
|
|
111
120
|
inDescendingOrder(snapPoints) || windowHeight === 0,
|
|
112
|
-
`Snap points need to be in descending order got: [${snapPoints
|
|
121
|
+
`Snap points need to be in descending order got: [${snapPoints.join(
|
|
122
|
+
', '
|
|
123
|
+
)}]`
|
|
113
124
|
);
|
|
114
125
|
}
|
|
115
126
|
|
|
116
|
-
const onDrag =
|
|
127
|
+
const onDrag = useEffectEvent((_, { delta }: PanInfo) => {
|
|
117
128
|
// Update drag indicator rotation based on drag velocity
|
|
118
129
|
const velocity = y.getVelocity();
|
|
119
130
|
|
|
@@ -124,20 +135,36 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
124
135
|
y.set(Math.max(y.get() + delta.y, 0));
|
|
125
136
|
});
|
|
126
137
|
|
|
127
|
-
const
|
|
138
|
+
const onDragStart = useEffectEvent(() => {
|
|
139
|
+
// Find focused input inside the sheet and blur it when dragging starts
|
|
140
|
+
// to prevent a weird ghost caret "bug" on mobile
|
|
141
|
+
const focusedElement = document.activeElement as HTMLElement | null;
|
|
142
|
+
if (!focusedElement || !sheetRef.current) return;
|
|
143
|
+
|
|
144
|
+
const isInput =
|
|
145
|
+
focusedElement.tagName === 'INPUT' ||
|
|
146
|
+
focusedElement.tagName === 'TEXTAREA';
|
|
147
|
+
|
|
148
|
+
// Only blur the focused element if it's inside the sheet
|
|
149
|
+
if (isInput && sheetRef.current.contains(focusedElement)) {
|
|
150
|
+
focusedElement.blur();
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const onDragEnd = useEffectEvent((_, { velocity }: PanInfo) => {
|
|
128
155
|
if (velocity.y > DRAG_VELOCITY_THRESHOLD) {
|
|
129
156
|
// User flicked the sheet down
|
|
130
157
|
onClose();
|
|
131
158
|
} else {
|
|
132
|
-
|
|
133
|
-
const sheetHeight =
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
160
|
+
const sheetHeight = sheetRef.current!.getBoundingClientRect().height;
|
|
134
161
|
const currentY = y.get();
|
|
135
162
|
|
|
136
163
|
let snapTo = 0;
|
|
137
164
|
|
|
138
165
|
if (snapPoints) {
|
|
139
166
|
const snapToValues = snapPoints.map(
|
|
140
|
-
p => sheetHeight - Math.min(p, sheetHeight)
|
|
167
|
+
(p) => sheetHeight - Math.min(p, sheetHeight)
|
|
141
168
|
);
|
|
142
169
|
|
|
143
170
|
// Allow snapping to the top of the sheet if detent is set to `content-height`
|
|
@@ -155,7 +182,7 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
155
182
|
snapTo = validateSnapTo({ snapTo, sheetHeight });
|
|
156
183
|
|
|
157
184
|
// Update the spring value so that the sheet is animated to the snap point
|
|
158
|
-
animate(y, snapTo, animationOptions);
|
|
185
|
+
void animate(y, snapTo, animationOptions);
|
|
159
186
|
|
|
160
187
|
if (snapPoints && onSnap) {
|
|
161
188
|
const snapValue = Math.abs(Math.round(snapPoints[0] - snapTo));
|
|
@@ -164,7 +191,7 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
164
191
|
}
|
|
165
192
|
|
|
166
193
|
const roundedSheetHeight = Math.round(sheetHeight);
|
|
167
|
-
const shouldClose = snapTo >= roundedSheetHeight;
|
|
194
|
+
const shouldClose = snapTo + 2 >= roundedSheetHeight; // 2px tolerance
|
|
168
195
|
|
|
169
196
|
if (shouldClose) onClose();
|
|
170
197
|
}
|
|
@@ -174,22 +201,18 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
174
201
|
});
|
|
175
202
|
|
|
176
203
|
// Trigger onSnap callback when sheet is opened or closed
|
|
177
|
-
|
|
204
|
+
useEffect(() => {
|
|
178
205
|
if (!snapPoints || !onSnap) return;
|
|
179
206
|
const snapIndex = isOpen ? initialSnap : snapPoints.length - 1;
|
|
180
207
|
onSnap(snapIndex);
|
|
181
208
|
}, [isOpen]); // eslint-disable-line
|
|
182
209
|
|
|
183
|
-
|
|
210
|
+
useImperativeHandle(ref, () => ({
|
|
184
211
|
y,
|
|
185
212
|
snapTo: (snapIndex: number) => {
|
|
186
|
-
const sheetEl = sheetRef.current
|
|
213
|
+
const sheetEl = sheetRef.current;
|
|
187
214
|
|
|
188
|
-
if (
|
|
189
|
-
snapPoints &&
|
|
190
|
-
snapPoints[snapIndex] !== undefined &&
|
|
191
|
-
sheetEl !== null
|
|
192
|
-
) {
|
|
215
|
+
if (snapPoints?.[snapIndex] !== undefined && sheetEl) {
|
|
193
216
|
const sheetHeight = sheetEl.getBoundingClientRect().height;
|
|
194
217
|
const snapPoint = snapPoints[snapIndex];
|
|
195
218
|
const snapTo = validateSnapTo({
|
|
@@ -197,7 +220,7 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
197
220
|
sheetHeight,
|
|
198
221
|
});
|
|
199
222
|
|
|
200
|
-
animate(y, snapTo, animationOptions);
|
|
223
|
+
void animate(y, snapTo, animationOptions);
|
|
201
224
|
if (onSnap) onSnap(snapIndex);
|
|
202
225
|
if (snapTo >= sheetHeight) onClose();
|
|
203
226
|
}
|
|
@@ -208,20 +231,21 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
208
231
|
|
|
209
232
|
// Framer Motion should handle body scroll locking but it's not working
|
|
210
233
|
// properly on iOS. Scroll locking from React Aria seems to work much better.
|
|
211
|
-
usePreventScroll({ isDisabled: disableScrollLocking
|
|
234
|
+
usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
|
|
212
235
|
|
|
213
|
-
const dragProps =
|
|
236
|
+
const dragProps = useMemo(() => {
|
|
214
237
|
const dragProps: SheetContextType['dragProps'] = {
|
|
215
238
|
drag: 'y',
|
|
216
239
|
dragElastic: 0,
|
|
217
240
|
dragMomentum: false,
|
|
218
241
|
dragPropagation: false,
|
|
219
242
|
onDrag,
|
|
243
|
+
onDragStart,
|
|
220
244
|
onDragEnd,
|
|
221
245
|
};
|
|
222
246
|
|
|
223
247
|
return disableDrag ? undefined : dragProps;
|
|
224
|
-
}, [disableDrag]); // eslint-disable-line
|
|
248
|
+
}, [disableDrag, windowHeight]); // eslint-disable-line
|
|
225
249
|
|
|
226
250
|
const context: SheetContextType = {
|
|
227
251
|
y,
|
|
@@ -250,8 +274,8 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
250
274
|
{/* NOTE: AnimatePresence requires us to set keys to children */}
|
|
251
275
|
{isOpen ? (
|
|
252
276
|
<SheetScrollerContextProvider>
|
|
253
|
-
{
|
|
254
|
-
|
|
277
|
+
{Children.map(children, (child: any, i) =>
|
|
278
|
+
cloneElement(child, { key: `sheet-child-${i}` })
|
|
255
279
|
)}
|
|
256
280
|
</SheetScrollerContextProvider>
|
|
257
281
|
) : null}
|
|
@@ -262,8 +286,10 @@ const Sheet = React.forwardRef<any, SheetProps>(
|
|
|
262
286
|
|
|
263
287
|
if (IS_SSR) return sheet;
|
|
264
288
|
|
|
265
|
-
return
|
|
289
|
+
return createPortal(sheet, mountPoint ?? document.body);
|
|
266
290
|
}
|
|
267
291
|
);
|
|
268
292
|
|
|
293
|
+
Sheet.displayName = 'Sheet';
|
|
294
|
+
|
|
269
295
|
export default Sheet;
|
package/src/styles.ts
CHANGED
package/src/types.tsx
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ForwardRefExoticComponent,
|
|
4
|
+
type HTMLAttributes,
|
|
5
|
+
type MutableRefObject,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
type RefAttributes,
|
|
8
|
+
} from 'react';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
type DragHandlers,
|
|
12
|
+
type MotionValue,
|
|
13
|
+
type MotionProps,
|
|
14
|
+
type motion,
|
|
15
|
+
type EasingDefinition,
|
|
16
|
+
type Transition,
|
|
8
17
|
} from 'framer-motion';
|
|
9
18
|
|
|
10
|
-
export
|
|
19
|
+
export interface SheetEvents {
|
|
11
20
|
onOpenStart?: () => void;
|
|
12
21
|
onOpenEnd?: () => void;
|
|
13
22
|
onCloseStart?: () => void;
|
|
14
23
|
onCloseEnd?: () => void;
|
|
15
24
|
onSnap?: (index: number) => void;
|
|
16
|
-
}
|
|
25
|
+
}
|
|
17
26
|
|
|
18
27
|
export type SheetDetent = 'full-height' | 'content-height';
|
|
19
28
|
|
|
@@ -21,14 +30,14 @@ type CommonProps = MotionProps & {
|
|
|
21
30
|
className?: string;
|
|
22
31
|
};
|
|
23
32
|
|
|
24
|
-
export
|
|
33
|
+
export interface SheetTweenConfig {
|
|
25
34
|
ease: EasingDefinition;
|
|
26
35
|
duration: number;
|
|
27
|
-
}
|
|
36
|
+
}
|
|
28
37
|
|
|
29
38
|
export type SheetProps = {
|
|
30
39
|
isOpen: boolean;
|
|
31
|
-
children:
|
|
40
|
+
children: ReactNode;
|
|
32
41
|
onClose: () => void;
|
|
33
42
|
rootId?: string;
|
|
34
43
|
mountPoint?: Element;
|
|
@@ -40,13 +49,13 @@ export type SheetProps = {
|
|
|
40
49
|
disableScrollLocking?: boolean;
|
|
41
50
|
prefersReducedMotion?: boolean;
|
|
42
51
|
} & SheetEvents &
|
|
43
|
-
|
|
52
|
+
ComponentPropsWithoutRef<typeof motion.div>;
|
|
44
53
|
|
|
45
54
|
export type SheetContainerProps = Omit<
|
|
46
55
|
CommonProps,
|
|
47
56
|
'initial' | 'animate' | 'exit' | 'onAnimationComplete'
|
|
48
57
|
> & {
|
|
49
|
-
children:
|
|
58
|
+
children: ReactNode;
|
|
50
59
|
};
|
|
51
60
|
|
|
52
61
|
export type SheetDraggableProps = Omit<
|
|
@@ -59,7 +68,7 @@ export type SheetDraggableProps = Omit<
|
|
|
59
68
|
| 'onDragStart'
|
|
60
69
|
| 'onDragEnd'
|
|
61
70
|
> & {
|
|
62
|
-
children?:
|
|
71
|
+
children?: ReactNode;
|
|
63
72
|
disableDrag?: boolean;
|
|
64
73
|
};
|
|
65
74
|
|
|
@@ -68,67 +77,68 @@ export type SheetBackdropProps = Omit<
|
|
|
68
77
|
'initial' | 'animate' | 'exit'
|
|
69
78
|
>;
|
|
70
79
|
|
|
71
|
-
export type SheetScrollerProps =
|
|
80
|
+
export type SheetScrollerProps = HTMLAttributes<HTMLDivElement> & {
|
|
72
81
|
draggableAt?: 'top' | 'bottom' | 'both';
|
|
73
82
|
};
|
|
74
83
|
|
|
75
|
-
export
|
|
84
|
+
export interface SheetDragProps {
|
|
76
85
|
drag: 'y';
|
|
77
86
|
dragElastic: number;
|
|
78
87
|
dragMomentum: boolean;
|
|
79
88
|
dragPropagation: boolean;
|
|
80
89
|
onDrag: DragHandlers['onDrag'];
|
|
90
|
+
onDragStart: DragHandlers['onDragStart'];
|
|
81
91
|
onDragEnd: DragHandlers['onDragEnd'];
|
|
82
|
-
}
|
|
92
|
+
}
|
|
83
93
|
|
|
84
|
-
export
|
|
94
|
+
export interface SheetContextType {
|
|
85
95
|
y: MotionValue<any>;
|
|
86
|
-
sheetRef:
|
|
96
|
+
sheetRef: MutableRefObject<any>;
|
|
87
97
|
isOpen: boolean;
|
|
88
98
|
snapPoints: SheetProps['snapPoints'];
|
|
89
99
|
detent: SheetDetent;
|
|
90
100
|
initialSnap: SheetProps['initialSnap'];
|
|
91
101
|
indicatorRotation: MotionValue<number>;
|
|
92
|
-
callbacks:
|
|
102
|
+
callbacks: MutableRefObject<SheetEvents>;
|
|
93
103
|
dragProps?: SheetDragProps;
|
|
94
104
|
windowHeight: number;
|
|
95
105
|
animationOptions: Transition;
|
|
96
106
|
reduceMotion: boolean;
|
|
97
107
|
disableDrag: boolean;
|
|
98
|
-
}
|
|
108
|
+
}
|
|
99
109
|
|
|
100
|
-
export
|
|
110
|
+
export interface SheetScrollerContextType {
|
|
101
111
|
disableDrag: boolean;
|
|
102
112
|
setDragDisabled: () => void;
|
|
103
113
|
setDragEnabled: () => void;
|
|
104
|
-
}
|
|
114
|
+
}
|
|
105
115
|
|
|
106
|
-
type ContainerComponent =
|
|
107
|
-
SheetContainerProps &
|
|
116
|
+
type ContainerComponent = ForwardRefExoticComponent<
|
|
117
|
+
SheetContainerProps & RefAttributes<any>
|
|
108
118
|
>;
|
|
109
119
|
|
|
110
|
-
type DraggableComponent =
|
|
111
|
-
SheetDraggableProps &
|
|
120
|
+
type DraggableComponent = ForwardRefExoticComponent<
|
|
121
|
+
SheetDraggableProps & RefAttributes<any>
|
|
112
122
|
>;
|
|
113
123
|
|
|
114
|
-
type BackdropComponent =
|
|
115
|
-
SheetBackdropProps &
|
|
124
|
+
type BackdropComponent = ForwardRefExoticComponent<
|
|
125
|
+
SheetBackdropProps & RefAttributes<any>
|
|
116
126
|
>;
|
|
117
127
|
|
|
118
|
-
type SheetComponent =
|
|
119
|
-
SheetProps &
|
|
128
|
+
type SheetComponent = ForwardRefExoticComponent<
|
|
129
|
+
SheetProps & RefAttributes<any>
|
|
120
130
|
>;
|
|
121
131
|
|
|
122
|
-
type ScrollerComponent =
|
|
123
|
-
SheetScrollerProps &
|
|
132
|
+
type ScrollerComponent = ForwardRefExoticComponent<
|
|
133
|
+
SheetScrollerProps & RefAttributes<any>
|
|
124
134
|
>;
|
|
125
135
|
|
|
126
|
-
|
|
136
|
+
interface SheetCompoundComponent {
|
|
127
137
|
Container: ContainerComponent;
|
|
128
138
|
Header: DraggableComponent;
|
|
129
139
|
Content: DraggableComponent;
|
|
130
140
|
Backdrop: BackdropComponent;
|
|
131
141
|
Scroller: ScrollerComponent;
|
|
132
|
-
}
|
|
142
|
+
}
|
|
133
143
|
|
|
134
144
|
export type SheetCompound = SheetComponent & SheetCompoundComponent;
|
|
@@ -11,8 +11,8 @@ interface PreventScrollOptions {
|
|
|
11
11
|
isDisabled?: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const visualViewport =
|
|
15
|
+
typeof window !== 'undefined' ? (window as any).visualViewport : undefined;
|
|
16
16
|
|
|
17
17
|
// HTML input types that do not cause the software keyboard to appear.
|
|
18
18
|
const nonTextInputTypes = new Set([
|
|
@@ -37,7 +37,7 @@ let restore: any;
|
|
|
37
37
|
* shift due to the scrollbars disappearing.
|
|
38
38
|
*/
|
|
39
39
|
export function usePreventScroll(options: PreventScrollOptions = {}) {
|
|
40
|
-
|
|
40
|
+
const { isDisabled } = options;
|
|
41
41
|
|
|
42
42
|
useLayoutEffect(() => {
|
|
43
43
|
if (isDisabled) {
|
|
@@ -104,7 +104,8 @@ function preventScrollStandard() {
|
|
|
104
104
|
function preventScrollMobileSafari() {
|
|
105
105
|
let scrollable: Element | undefined;
|
|
106
106
|
let lastY = 0;
|
|
107
|
-
|
|
107
|
+
|
|
108
|
+
const onTouchStart = (e: TouchEvent) => {
|
|
108
109
|
// Store the nearest scrollable parent element from the element that the user touched.
|
|
109
110
|
scrollable = getScrollParent(e.target as Element);
|
|
110
111
|
if (
|
|
@@ -117,7 +118,7 @@ function preventScrollMobileSafari() {
|
|
|
117
118
|
lastY = e.changedTouches[0].pageY;
|
|
118
119
|
};
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
const onTouchMove = (e: TouchEvent) => {
|
|
121
122
|
// In special situations, `onTouchStart` may be called without `onTouchStart` being called.
|
|
122
123
|
// (e.g. when the user places a finger on the screen before the <Sheet> is mounted and then moves the finger after it is mounted).
|
|
123
124
|
// If `onTouchStart` is not called, `scrollable` is `undefined`. Therefore, such cases are ignored.
|
|
@@ -138,9 +139,9 @@ function preventScrollMobileSafari() {
|
|
|
138
139
|
// of a nested scrollable area, otherwise mobile Safari will start scrolling
|
|
139
140
|
// the window instead. Unfortunately, this disables bounce scrolling when at
|
|
140
141
|
// the top but it's the best we can do.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
const y = e.changedTouches[0].pageY;
|
|
143
|
+
const scrollTop = scrollable.scrollTop;
|
|
144
|
+
const bottom = scrollable.scrollHeight - scrollable.clientHeight;
|
|
144
145
|
|
|
145
146
|
// Fix for: https://github.com/adobe/react-spectrum/pull/3780/files
|
|
146
147
|
if (bottom === 0) {
|
|
@@ -154,8 +155,8 @@ function preventScrollMobileSafari() {
|
|
|
154
155
|
lastY = y;
|
|
155
156
|
};
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
const onTouchEnd = (e: TouchEvent) => {
|
|
159
|
+
const target = e.target as HTMLElement;
|
|
159
160
|
|
|
160
161
|
// Apply this change if we're not already focused on the target element
|
|
161
162
|
if (willOpenKeyboard(target) && target !== document.activeElement) {
|
|
@@ -172,8 +173,8 @@ function preventScrollMobileSafari() {
|
|
|
172
173
|
}
|
|
173
174
|
};
|
|
174
175
|
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
const onFocus = (e: FocusEvent) => {
|
|
177
|
+
const target = e.target as HTMLElement;
|
|
177
178
|
if (willOpenKeyboard(target)) {
|
|
178
179
|
// Transform also needs to be applied in the focus event in cases where focus moves
|
|
179
180
|
// other than tapping on an input directly, e.g. the next/previous buttons in the
|
|
@@ -197,7 +198,9 @@ function preventScrollMobileSafari() {
|
|
|
197
198
|
// measure the correct position to scroll to.
|
|
198
199
|
visualViewport.addEventListener(
|
|
199
200
|
'resize',
|
|
200
|
-
() =>
|
|
201
|
+
() => {
|
|
202
|
+
scrollIntoView(target);
|
|
203
|
+
},
|
|
201
204
|
{ once: true }
|
|
202
205
|
);
|
|
203
206
|
}
|
|
@@ -206,7 +209,7 @@ function preventScrollMobileSafari() {
|
|
|
206
209
|
}
|
|
207
210
|
};
|
|
208
211
|
|
|
209
|
-
|
|
212
|
+
const onWindowScroll = () => {
|
|
210
213
|
// Last resort. If the window scrolled, scroll it back to the top.
|
|
211
214
|
// It should always be at the top because the body will have a negative margin (see below).
|
|
212
215
|
window.scrollTo(0, 0);
|
|
@@ -215,10 +218,10 @@ function preventScrollMobileSafari() {
|
|
|
215
218
|
// Record the original scroll position so we can restore it.
|
|
216
219
|
// Then apply a negative margin to the body to offset it by the scroll position. This will
|
|
217
220
|
// enable us to scroll the window to the top, which is required for the rest of this to work.
|
|
218
|
-
|
|
219
|
-
|
|
221
|
+
const scrollX = window.pageXOffset;
|
|
222
|
+
const scrollY = window.pageYOffset;
|
|
220
223
|
|
|
221
|
-
|
|
224
|
+
const restoreStyles = chain(
|
|
222
225
|
setStyle(
|
|
223
226
|
document.documentElement,
|
|
224
227
|
'paddingRight',
|
|
@@ -231,7 +234,7 @@ function preventScrollMobileSafari() {
|
|
|
231
234
|
// Scroll to the top. The negative margin on the body will make this appear the same.
|
|
232
235
|
window.scrollTo(0, 0);
|
|
233
236
|
|
|
234
|
-
|
|
237
|
+
const removeEvents = chain(
|
|
235
238
|
addEvent(document, 'touchstart', onTouchStart, {
|
|
236
239
|
passive: false,
|
|
237
240
|
capture: true,
|
|
@@ -258,7 +261,7 @@ function preventScrollMobileSafari() {
|
|
|
258
261
|
|
|
259
262
|
// Sets a CSS property on an element, and returns a function to revert it to the previous value.
|
|
260
263
|
function setStyle(element: any, style: string, value: string) {
|
|
261
|
-
|
|
264
|
+
const cur = element.style[style];
|
|
262
265
|
element.style[style] = value;
|
|
263
266
|
|
|
264
267
|
return () => {
|
|
@@ -280,17 +283,17 @@ function addEvent<K extends keyof GlobalEventHandlersEventMap>(
|
|
|
280
283
|
}
|
|
281
284
|
|
|
282
285
|
function scrollIntoView(target: Element) {
|
|
283
|
-
|
|
286
|
+
const root = document.scrollingElement || document.documentElement;
|
|
284
287
|
while (target && target !== root) {
|
|
285
288
|
// Find the parent scrollable element and adjust the scroll position if the target is not already in view.
|
|
286
|
-
|
|
289
|
+
const scrollable = getScrollParent(target);
|
|
287
290
|
if (
|
|
288
291
|
scrollable !== document.documentElement &&
|
|
289
292
|
scrollable !== document.body &&
|
|
290
293
|
scrollable !== target
|
|
291
294
|
) {
|
|
292
|
-
|
|
293
|
-
|
|
295
|
+
const scrollableTop = scrollable.getBoundingClientRect().top;
|
|
296
|
+
const targetTop = target.getBoundingClientRect().top;
|
|
294
297
|
if (targetTop > scrollableTop + target.clientHeight) {
|
|
295
298
|
scrollable.scrollTop += targetTop - scrollableTop;
|
|
296
299
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type ForwardedRef, type RefCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
export function getClosest(nums: number[], goal: number) {
|
|
4
4
|
let closest = nums[0];
|
|
@@ -74,8 +74,9 @@ export function validateSnapTo({
|
|
|
74
74
|
}) {
|
|
75
75
|
if (snapTo < 0) {
|
|
76
76
|
console.warn(
|
|
77
|
-
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${
|
|
78
|
-
Math.abs(snapTo)
|
|
77
|
+
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${
|
|
78
|
+
sheetHeight + Math.abs(snapTo)
|
|
79
|
+
}.`
|
|
79
80
|
);
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -83,8 +84,8 @@ export function validateSnapTo({
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export function mergeRefs<T = any>(
|
|
86
|
-
refs:
|
|
87
|
-
):
|
|
87
|
+
refs: Array<ForwardedRef<T>>
|
|
88
|
+
): RefCallback<T> {
|
|
88
89
|
return (value: any) => {
|
|
89
90
|
refs.forEach((ref: any) => {
|
|
90
91
|
if (typeof ref === 'function') {
|
package/dist/SheetBackdrop.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
declare const SheetBackdrop: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps & {
|
|
3
|
-
className?: string | undefined;
|
|
4
|
-
}, "style" | "transformTemplate" | "transformValues" | "children" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "drag" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragConstraints" | "dragElastic" | "dragMomentum" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDragStart" | "onDragEnd" | "onDrag" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "layoutDependency" | "layoutScroll" | "layoutRoot" | "custom" | "inherit" | "ignoreStrict" | "className"> & React.RefAttributes<any>>;
|
|
5
|
-
export default SheetBackdrop;
|
package/dist/SheetContainer.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
declare const SheetContainer: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps & {
|
|
3
|
-
className?: string | undefined;
|
|
4
|
-
}, "style" | "transformTemplate" | "transformValues" | "children" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "drag" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragConstraints" | "dragElastic" | "dragMomentum" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDragStart" | "onDragEnd" | "onDrag" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "layoutDependency" | "layoutScroll" | "layoutRoot" | "custom" | "inherit" | "ignoreStrict" | "className"> & {
|
|
5
|
-
children: React.ReactNode;
|
|
6
|
-
} & React.RefAttributes<any>>;
|
|
7
|
-
export default SheetContainer;
|
package/dist/SheetContent.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
declare const SheetContent: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps & {
|
|
3
|
-
className?: string | undefined;
|
|
4
|
-
}, "style" | "transformTemplate" | "transformValues" | "children" | "initial" | "animate" | "exit" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "layoutDependency" | "layoutScroll" | "layoutRoot" | "custom" | "inherit" | "ignoreStrict" | "className"> & {
|
|
5
|
-
children?: React.ReactNode;
|
|
6
|
-
disableDrag?: boolean | undefined;
|
|
7
|
-
} & React.RefAttributes<any>>;
|
|
8
|
-
export default SheetContent;
|
package/dist/SheetHeader.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
declare const SheetHeader: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps & {
|
|
3
|
-
className?: string | undefined;
|
|
4
|
-
}, "style" | "transformTemplate" | "transformValues" | "children" | "initial" | "animate" | "exit" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "layoutDependency" | "layoutScroll" | "layoutRoot" | "custom" | "inherit" | "ignoreStrict" | "className"> & {
|
|
5
|
-
children?: React.ReactNode;
|
|
6
|
-
disableDrag?: boolean | undefined;
|
|
7
|
-
} & React.RefAttributes<any>>;
|
|
8
|
-
export default SheetHeader;
|
package/dist/SheetScroller.d.ts
DELETED
package/dist/constants.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const MAX_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
|
|
2
|
-
export declare const IS_SSR: boolean;
|
|
3
|
-
export declare const DEFAULT_TWEEN_CONFIG: {
|
|
4
|
-
ease: string;
|
|
5
|
-
duration: number;
|
|
6
|
-
};
|
|
7
|
-
export declare const REDUCED_MOTION_TWEEN_CONFIG: {
|
|
8
|
-
ease: string;
|
|
9
|
-
duration: number;
|
|
10
|
-
};
|
|
11
|
-
export declare const DRAG_CLOSE_THRESHOLD = 0.6;
|
|
12
|
-
export declare const DRAG_VELOCITY_THRESHOLD = 500;
|