react-modal-sheet 4.4.0 → 5.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 +332 -188
- package/dist/index.d.mts +51 -39
- package/dist/index.d.ts +51 -39
- package/dist/index.js +854 -585
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +847 -579
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/SheetBackdrop.tsx +19 -6
- package/src/SheetContainer.tsx +34 -25
- package/src/SheetContent.tsx +77 -14
- package/src/SheetDragIndicator.tsx +52 -0
- package/src/SheetHeader.tsx +30 -30
- package/src/constants.ts +5 -3
- package/src/context.tsx +2 -42
- package/src/hooks/use-dimensions.ts +4 -4
- package/src/hooks/use-drag-constraints.ts +3 -3
- package/src/hooks/use-modal-effect.ts +19 -23
- package/src/hooks/use-resize-observer.ts +31 -0
- package/src/hooks/use-scroll-position.ts +72 -0
- package/src/hooks/use-sheet-state.ts +63 -0
- package/src/hooks/use-virtual-keyboard.ts +153 -0
- package/src/index.tsx +14 -10
- package/src/sheet.tsx +247 -209
- package/src/snap.ts +226 -0
- package/src/styles.ts +94 -53
- package/src/types.tsx +83 -79
- package/src/utils.ts +8 -149
- package/src/SheetScroller.tsx +0 -96
- package/src/hooks/use-event-callbacks.ts +0 -44
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { type RefObject, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useStableCallback } from './use-stable-callback';
|
|
3
|
+
|
|
4
|
+
type VirtualKeyboardState = {
|
|
5
|
+
isVisible: boolean;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type UseVirtualKeyboardOptions = {
|
|
10
|
+
/**
|
|
11
|
+
* Ref to the container element to apply `keyboard-inset-height` CSS variable updates (required)
|
|
12
|
+
*/
|
|
13
|
+
containerRef: RefObject<HTMLDivElement | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Enable or disable the hook entirely (default: true)
|
|
16
|
+
*/
|
|
17
|
+
isEnabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Minimum pixel height difference to consider the keyboard visible (default: 100px)
|
|
20
|
+
*/
|
|
21
|
+
visualViewportThreshold?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to treat contenteditable elements as text inputs (default: true)
|
|
24
|
+
*/
|
|
25
|
+
includeContentEditable?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Delay in ms for debouncing viewport changes (default: 100ms)
|
|
28
|
+
*/
|
|
29
|
+
debounceDelay?: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function useVirtualKeyboard({
|
|
33
|
+
containerRef,
|
|
34
|
+
isEnabled = true,
|
|
35
|
+
debounceDelay = 100,
|
|
36
|
+
includeContentEditable = true,
|
|
37
|
+
visualViewportThreshold = 100,
|
|
38
|
+
}: UseVirtualKeyboardOptions) {
|
|
39
|
+
const [state, setState] = useState<VirtualKeyboardState>({
|
|
40
|
+
isVisible: false,
|
|
41
|
+
height: 0,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const focusedElementRef = useRef<HTMLElement | null>(null);
|
|
45
|
+
const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
46
|
+
|
|
47
|
+
const isTextInput = useStableCallback((el: Element | null) => {
|
|
48
|
+
return (
|
|
49
|
+
el?.tagName === 'INPUT' ||
|
|
50
|
+
el?.tagName === 'TEXTAREA' ||
|
|
51
|
+
(includeContentEditable &&
|
|
52
|
+
el instanceof HTMLElement &&
|
|
53
|
+
el.isContentEditable)
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!isEnabled) return;
|
|
59
|
+
|
|
60
|
+
const vv = window.visualViewport;
|
|
61
|
+
const vk = (navigator as any).virtualKeyboard;
|
|
62
|
+
|
|
63
|
+
function setKeyboardInsetHeightEnv(height: number) {
|
|
64
|
+
containerRef.current?.style.setProperty(
|
|
65
|
+
'--keyboard-inset-height',
|
|
66
|
+
`${height}px`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function handleFocusIn(e: FocusEvent) {
|
|
71
|
+
if (e.target instanceof HTMLElement && isTextInput(e.target)) {
|
|
72
|
+
focusedElementRef.current = e.target;
|
|
73
|
+
updateKeyboardState();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function handleFocusOut() {
|
|
78
|
+
focusedElementRef.current = null;
|
|
79
|
+
updateKeyboardState();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function updateKeyboardState() {
|
|
83
|
+
if (debounceTimer.current) {
|
|
84
|
+
clearTimeout(debounceTimer.current);
|
|
85
|
+
}
|
|
86
|
+
debounceTimer.current = setTimeout(() => {
|
|
87
|
+
const active = focusedElementRef.current;
|
|
88
|
+
const inputIsFocused = isTextInput(active);
|
|
89
|
+
|
|
90
|
+
if (!inputIsFocused) {
|
|
91
|
+
setKeyboardInsetHeightEnv(0);
|
|
92
|
+
setState({ isVisible: false, height: 0 });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (vv) {
|
|
97
|
+
const heightDiff = window.innerHeight - vv.height;
|
|
98
|
+
|
|
99
|
+
if (heightDiff > visualViewportThreshold) {
|
|
100
|
+
setKeyboardInsetHeightEnv(heightDiff);
|
|
101
|
+
setState({ isVisible: true, height: heightDiff });
|
|
102
|
+
} else {
|
|
103
|
+
setKeyboardInsetHeightEnv(0);
|
|
104
|
+
setState({ isVisible: false, height: 0 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, debounceDelay);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
window.addEventListener('focusin', handleFocusIn);
|
|
111
|
+
window.addEventListener('focusout', handleFocusOut);
|
|
112
|
+
|
|
113
|
+
if (vv) {
|
|
114
|
+
vv.addEventListener('resize', updateKeyboardState);
|
|
115
|
+
vv.addEventListener('scroll', updateKeyboardState);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let currentOverlaysContent = false;
|
|
119
|
+
|
|
120
|
+
if (vk) {
|
|
121
|
+
currentOverlaysContent = vk.overlaysContent;
|
|
122
|
+
vk.overlaysContent = true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return () => {
|
|
126
|
+
window.removeEventListener('focusin', handleFocusIn);
|
|
127
|
+
window.removeEventListener('focusout', handleFocusOut);
|
|
128
|
+
|
|
129
|
+
if (vv) {
|
|
130
|
+
vv.removeEventListener('resize', updateKeyboardState);
|
|
131
|
+
vv.removeEventListener('scroll', updateKeyboardState);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (vk) {
|
|
135
|
+
vk.overlaysContent = currentOverlaysContent;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (debounceTimer.current) {
|
|
139
|
+
clearTimeout(debounceTimer.current);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}, [
|
|
143
|
+
debounceDelay,
|
|
144
|
+
includeContentEditable,
|
|
145
|
+
isEnabled,
|
|
146
|
+
visualViewportThreshold,
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
keyboardHeight: state.height,
|
|
151
|
+
isKeyboardOpen: state.isVisible,
|
|
152
|
+
};
|
|
153
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
import type { MotionValue } from 'motion/react';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { Sheet as SheetBase } from './sheet';
|
|
3
|
+
import { SheetBackdrop } from './SheetBackdrop';
|
|
5
4
|
import { SheetContainer } from './SheetContainer';
|
|
6
5
|
import { SheetContent } from './SheetContent';
|
|
6
|
+
import { SheetDragIndicator } from './SheetDragIndicator';
|
|
7
7
|
import { SheetHeader } from './SheetHeader';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { Sheet as SheetBase } from './sheet';
|
|
9
|
+
import type { SheetCompound } from './types';
|
|
10
10
|
|
|
11
11
|
export interface SheetRef {
|
|
12
12
|
y: MotionValue<number>;
|
|
13
|
-
|
|
13
|
+
yInverted: MotionValue<number>;
|
|
14
|
+
height: number;
|
|
15
|
+
snapTo: (index: number) => Promise<void>;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const Sheet: SheetCompound = Object.assign(SheetBase, {
|
|
17
19
|
Container: SheetContainer,
|
|
18
20
|
Header: SheetHeader,
|
|
21
|
+
DragIndicator: SheetDragIndicator,
|
|
19
22
|
Content: SheetContent,
|
|
20
23
|
Backdrop: SheetBackdrop,
|
|
21
|
-
Scroller: SheetScroller,
|
|
22
24
|
});
|
|
23
25
|
|
|
24
26
|
// Export types
|
|
25
|
-
export type SheetHeaderProps = SheetDraggableProps;
|
|
26
|
-
export type SheetContentProps = SheetDraggableProps;
|
|
27
27
|
export type {
|
|
28
|
-
SheetProps,
|
|
29
28
|
SheetBackdropProps,
|
|
30
|
-
SheetScrollerProps,
|
|
31
29
|
SheetContainerProps,
|
|
30
|
+
SheetContentProps,
|
|
31
|
+
SheetDragIndicatorProps,
|
|
32
|
+
SheetHeaderProps,
|
|
33
|
+
SheetProps,
|
|
34
|
+
SheetSnapPoint,
|
|
35
|
+
SheetStateInfo,
|
|
32
36
|
} from './types';
|