react-modal-sheet 5.2.1 → 5.2.2
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/dist/index.js +71 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +72 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/SheetContent.tsx +10 -1
- package/src/debug.ts +37 -0
- package/src/hooks/use-scroll-position.ts +63 -49
package/src/debug.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This library runs on mobile devices so we can't use console.log for debugging. Instead, we can use this function to send debug information to a remote server or store it locally on the device.
|
|
3
|
+
* Instead display the "payload" in a fixed position element on the screen
|
|
4
|
+
* and hide it when that element is clicked. Create the element if it doesn't exist,
|
|
5
|
+
* and update its content with the payload (any JSON data).
|
|
6
|
+
*/
|
|
7
|
+
export function debug(payload: any) {
|
|
8
|
+
let debugElement = document.getElementById('debug-element');
|
|
9
|
+
|
|
10
|
+
if (!debugElement) {
|
|
11
|
+
debugElement = document.createElement('div');
|
|
12
|
+
debugElement.id = 'debug-element';
|
|
13
|
+
debugElement.style.position = 'fixed';
|
|
14
|
+
debugElement.style.bottom = '0';
|
|
15
|
+
debugElement.style.left = '0';
|
|
16
|
+
debugElement.style.width = '100%';
|
|
17
|
+
debugElement.style.maxHeight = '50%';
|
|
18
|
+
debugElement.style.overflowY = 'auto';
|
|
19
|
+
debugElement.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
|
|
20
|
+
debugElement.style.color = 'white';
|
|
21
|
+
debugElement.style.padding = '10px';
|
|
22
|
+
debugElement.style.fontSize = '12px';
|
|
23
|
+
debugElement.style.zIndex = '9999';
|
|
24
|
+
debugElement.style.cursor = 'pointer';
|
|
25
|
+
document.body.appendChild(debugElement);
|
|
26
|
+
|
|
27
|
+
debugElement.addEventListener('click', () => {
|
|
28
|
+
if (debugElement) {
|
|
29
|
+
debugElement.style.display =
|
|
30
|
+
debugElement.style.display === 'none' ? 'block' : 'none';
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
debugElement.textContent = JSON.stringify(payload, null, 2);
|
|
36
|
+
debugElement.style.display = 'block';
|
|
37
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { debug } from '../debug';
|
|
3
|
+
import { useStableCallback } from './use-stable-callback';
|
|
2
4
|
|
|
3
5
|
type UseScrollPositionOptions = {
|
|
4
6
|
/**
|
|
@@ -46,68 +48,80 @@ type UseScrollPositionOptions = {
|
|
|
46
48
|
export function useScrollPosition(options: UseScrollPositionOptions = {}) {
|
|
47
49
|
const { debounceDelay = 32, isEnabled = true } = options;
|
|
48
50
|
|
|
51
|
+
const scrollTimeoutRef = useRef<number | null>(null);
|
|
49
52
|
const [element, setElement] = useState<HTMLElement | null>(null);
|
|
50
53
|
const [scrollPosition, setScrollPosition] = useState<
|
|
51
54
|
'top' | 'bottom' | 'middle' | undefined
|
|
52
55
|
>(undefined);
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
const scrollRef = useMemo(
|
|
58
|
+
() => (element: HTMLElement | null) => {
|
|
59
|
+
setElement(element);
|
|
60
|
+
},
|
|
61
|
+
[]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const determineScrollPosition = useStableCallback((element: HTMLElement) => {
|
|
65
|
+
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
66
|
+
const isScrollable = scrollHeight > clientHeight;
|
|
67
|
+
|
|
68
|
+
if (!isScrollable) {
|
|
69
|
+
// Reset scroll position if the content is not scrollable anymore
|
|
70
|
+
if (scrollPosition) setScrollPosition(undefined);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
56
73
|
|
|
57
|
-
|
|
74
|
+
const isAtTop = scrollTop <= 0;
|
|
75
|
+
const isAtBottom =
|
|
76
|
+
Math.ceil(scrollHeight) - Math.ceil(scrollTop) ===
|
|
77
|
+
Math.ceil(clientHeight);
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
61
|
-
const isScrollable = scrollHeight > clientHeight;
|
|
79
|
+
let position: 'top' | 'bottom' | 'middle';
|
|
62
80
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
if (isAtTop) {
|
|
82
|
+
position = 'top';
|
|
83
|
+
} else if (isAtBottom) {
|
|
84
|
+
position = 'bottom';
|
|
85
|
+
} else {
|
|
86
|
+
position = 'middle';
|
|
87
|
+
}
|
|
68
88
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Math.ceil(scrollHeight) - Math.ceil(scrollTop) ===
|
|
72
|
-
Math.ceil(clientHeight);
|
|
89
|
+
// Let browser only handle downward pan gestures (scrolling content up)
|
|
90
|
+
element.style.touchAction = isAtTop ? 'pan-down' : '';
|
|
73
91
|
|
|
74
|
-
|
|
92
|
+
if (position === scrollPosition) return;
|
|
93
|
+
setScrollPosition(position);
|
|
94
|
+
});
|
|
75
95
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
position = 'bottom';
|
|
80
|
-
} else {
|
|
81
|
-
position = 'middle';
|
|
82
|
-
}
|
|
96
|
+
const onScroll = useStableCallback((event: Event) => {
|
|
97
|
+
if (event.currentTarget instanceof HTMLElement) {
|
|
98
|
+
const el = event.currentTarget;
|
|
83
99
|
|
|
84
|
-
if (
|
|
85
|
-
setScrollPosition(position);
|
|
86
|
-
}
|
|
100
|
+
if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
} else {
|
|
97
|
-
// Debounce the scroll handler
|
|
98
|
-
scrollTimeout = setTimeout(
|
|
99
|
-
() => determineScrollPosition(el),
|
|
100
|
-
debounceDelay
|
|
101
|
-
);
|
|
102
|
-
}
|
|
102
|
+
if (debounceDelay === 0) {
|
|
103
|
+
determineScrollPosition(el);
|
|
104
|
+
} else {
|
|
105
|
+
// Debounce the scroll handler
|
|
106
|
+
scrollTimeoutRef.current = setTimeout(
|
|
107
|
+
() => determineScrollPosition(el),
|
|
108
|
+
debounceDelay
|
|
109
|
+
);
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const onTouchStart = useStableCallback((event: Event) => {
|
|
115
|
+
if (event.currentTarget instanceof HTMLElement) {
|
|
116
|
+
const element = event.currentTarget;
|
|
117
|
+
requestAnimationFrame(() => {
|
|
118
|
+
determineScrollPosition(element);
|
|
119
|
+
});
|
|
110
120
|
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (!element || !isEnabled) return;
|
|
111
125
|
|
|
112
126
|
// Determine initial scroll position
|
|
113
127
|
determineScrollPosition(element);
|
|
@@ -116,14 +130,14 @@ export function useScrollPosition(options: UseScrollPositionOptions = {}) {
|
|
|
116
130
|
element.addEventListener('touchstart', onTouchStart);
|
|
117
131
|
|
|
118
132
|
return () => {
|
|
119
|
-
if (
|
|
133
|
+
if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
|
|
120
134
|
element.removeEventListener('scroll', onScroll);
|
|
121
135
|
element.removeEventListener('touchstart', onTouchStart);
|
|
122
136
|
};
|
|
123
137
|
}, [element, isEnabled]);
|
|
124
138
|
|
|
125
139
|
return {
|
|
126
|
-
scrollRef
|
|
140
|
+
scrollRef,
|
|
127
141
|
scrollPosition,
|
|
128
142
|
};
|
|
129
143
|
}
|