react-modal-sheet 5.3.1 → 5.4.1
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 +9 -6
- package/dist/index.d.mts +0 -5
- package/dist/index.d.ts +0 -5
- package/dist/index.js +253 -160
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +254 -161
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/SheetBackdrop.tsx +10 -4
- package/src/SheetContent.tsx +9 -3
- package/src/debug.ts +3 -2
- package/src/hooks/use-keyboard-avoidance.ts +75 -0
- package/src/hooks/use-prevent-scroll.ts +5 -27
- package/src/hooks/use-scroll-position.ts +0 -1
- package/src/hooks/use-virtual-keyboard.ts +127 -52
- package/src/sheet.tsx +59 -73
- package/src/types.tsx +2 -2
- package/src/utils.ts +26 -0
package/README.md
CHANGED
|
@@ -423,18 +423,21 @@ function KeyboardExample() {
|
|
|
423
423
|
}
|
|
424
424
|
```
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
Virtual keyboard avoidance consists of several key features:
|
|
427
427
|
|
|
428
428
|
- Uses the `env(keyboard-inset-height)` from [Virtual Keyboard API](https://developer.mozilla.org/en-US/docs/Web/API/VirtualKeyboard_API) and fallbacks to [Visual Viewport API](https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API) and custom `keyboard-inset-height` CSS environment variable.
|
|
429
429
|
- Works automatically on mobile devices with virtual keyboard support.
|
|
430
430
|
- Applies dynamic padding to keep content visible when the keyboard opens.
|
|
431
|
+
- Scrolls the focused input into view if it's covered by the keyboard.
|
|
431
432
|
- Disables drag gestures while the keyboard is open to prevent glitches.
|
|
432
|
-
- When using snap points
|
|
433
|
+
- When using snap points, the sheet will be automatically moved up to the last snap point to ensure that all content is reachable (even when the content is scrollable) within the sheet when the keyboard is open.
|
|
433
434
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
>
|
|
437
|
-
>
|
|
435
|
+
> [!NOTE]
|
|
436
|
+
> Avoiding the virtual keyboard is a complex problem and the built-in avoidance may not work perfectly in all cases or on all devices. The library does its best to handle the most common scenarios but there may be edge cases where it doesn't work as expected.
|
|
437
|
+
>
|
|
438
|
+
> Mobile browsers, eg. Android Chrome, may try to automatically move the focused input into view when the keyboard opens which might interfere with the built-in keyboard avoidance behavior causing the sheet content to jump around.
|
|
439
|
+
>
|
|
440
|
+
> If you need to disable the built-in keyboard avoidance, set `avoidKeyboard={false}`.
|
|
438
441
|
|
|
439
442
|
#### Managing virtual keyboard manually
|
|
440
443
|
|
package/dist/index.d.mts
CHANGED
|
@@ -139,11 +139,6 @@ type UseVirtualKeyboardOptions = {
|
|
|
139
139
|
* @default 100
|
|
140
140
|
*/
|
|
141
141
|
visualViewportThreshold?: number;
|
|
142
|
-
/**
|
|
143
|
-
* Whether to treat contenteditable elements as text inputs.
|
|
144
|
-
* @default true
|
|
145
|
-
*/
|
|
146
|
-
includeContentEditable?: boolean;
|
|
147
142
|
/**
|
|
148
143
|
* Delay in ms for debouncing viewport changes.
|
|
149
144
|
* @default 100
|
package/dist/index.d.ts
CHANGED
|
@@ -139,11 +139,6 @@ type UseVirtualKeyboardOptions = {
|
|
|
139
139
|
* @default 100
|
|
140
140
|
*/
|
|
141
141
|
visualViewportThreshold?: number;
|
|
142
|
-
/**
|
|
143
|
-
* Whether to treat contenteditable elements as text inputs.
|
|
144
|
-
* @default true
|
|
145
|
-
*/
|
|
146
|
-
includeContentEditable?: boolean;
|
|
147
142
|
/**
|
|
148
143
|
* Delay in ms for debouncing viewport changes.
|
|
149
144
|
* @default 100
|
package/dist/index.js
CHANGED
|
@@ -203,6 +203,23 @@ function waitForElement(className, interval = 50, maxAttempts = 20) {
|
|
|
203
203
|
}, interval);
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
+
var nonTextInputTypes = /* @__PURE__ */ new Set([
|
|
207
|
+
"checkbox",
|
|
208
|
+
"radio",
|
|
209
|
+
"range",
|
|
210
|
+
"color",
|
|
211
|
+
"file",
|
|
212
|
+
"image",
|
|
213
|
+
"button",
|
|
214
|
+
"submit",
|
|
215
|
+
"reset"
|
|
216
|
+
]);
|
|
217
|
+
function willOpenKeyboard(target) {
|
|
218
|
+
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
219
|
+
}
|
|
220
|
+
function isHTTPS() {
|
|
221
|
+
return typeof window !== "undefined" && window.isSecureContext;
|
|
222
|
+
}
|
|
206
223
|
|
|
207
224
|
// src/SheetBackdrop.tsx
|
|
208
225
|
var isClickable = (props) => !!props.onClick || !!props.onTap;
|
|
@@ -218,13 +235,16 @@ var SheetBackdrop = React4.forwardRef(
|
|
|
218
235
|
...style,
|
|
219
236
|
pointerEvents
|
|
220
237
|
};
|
|
238
|
+
const animationProps = sheetContext.prefersReducedMotion ? {} : {
|
|
239
|
+
initial: { opacity: 0 },
|
|
240
|
+
animate: { opacity: 1 },
|
|
241
|
+
exit: { opacity: 0 },
|
|
242
|
+
transition: { duration: 1 }
|
|
243
|
+
};
|
|
221
244
|
return /* @__PURE__ */ React4__default.default.createElement(
|
|
222
245
|
Comp,
|
|
223
246
|
{
|
|
224
|
-
|
|
225
|
-
animate: { opacity: 1 },
|
|
226
|
-
exit: { opacity: 0 },
|
|
227
|
-
transition: { duration: 1 },
|
|
247
|
+
...animationProps,
|
|
228
248
|
...rest,
|
|
229
249
|
ref,
|
|
230
250
|
className: `react-modal-sheet-backdrop ${className}`,
|
|
@@ -400,7 +420,7 @@ var SheetContent = React4.forwardRef(
|
|
|
400
420
|
};
|
|
401
421
|
const scrollStyle = applyStyles(styles.scroller, isUnstyled);
|
|
402
422
|
if (sheetContext.avoidKeyboard) {
|
|
403
|
-
scrollStyle.paddingBottom = "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))";
|
|
423
|
+
scrollStyle.paddingBottom = isHTTPS() ? "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))" : "var(--keyboard-inset-height, 0px)";
|
|
404
424
|
}
|
|
405
425
|
if (disableScroll) {
|
|
406
426
|
scrollStyle.overflowY = "hidden";
|
|
@@ -523,6 +543,195 @@ function useDimensions() {
|
|
|
523
543
|
}, []);
|
|
524
544
|
return dimensions;
|
|
525
545
|
}
|
|
546
|
+
var virtualKeyboardOverlayUsers = 0;
|
|
547
|
+
var initialVirtualKeyboardOverlaysContent = null;
|
|
548
|
+
function useVirtualKeyboard(options = {}) {
|
|
549
|
+
const {
|
|
550
|
+
containerRef,
|
|
551
|
+
isEnabled = true,
|
|
552
|
+
debounceDelay = 100,
|
|
553
|
+
visualViewportThreshold = 100
|
|
554
|
+
} = options;
|
|
555
|
+
const [state, setState] = React4.useState({
|
|
556
|
+
isVisible: false,
|
|
557
|
+
height: 0
|
|
558
|
+
});
|
|
559
|
+
const focusedElementRef = React4.useRef(null);
|
|
560
|
+
const debounceTimer = React4.useRef(null);
|
|
561
|
+
React4.useEffect(() => {
|
|
562
|
+
const vv = window.visualViewport;
|
|
563
|
+
const vk = getVirtualKeyboardApi();
|
|
564
|
+
function setKeyboardInsetHeightEnv(height) {
|
|
565
|
+
const element = (containerRef == null ? void 0 : containerRef.current) || document.documentElement;
|
|
566
|
+
if (vk) {
|
|
567
|
+
element.style.setProperty(
|
|
568
|
+
"--keyboard-inset-height",
|
|
569
|
+
`env(keyboard-inset-height, ${height}px)`
|
|
570
|
+
);
|
|
571
|
+
} else {
|
|
572
|
+
element.style.setProperty("--keyboard-inset-height", `${height}px`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function setKeyboardState(nextState) {
|
|
576
|
+
setState(
|
|
577
|
+
(prevState) => prevState.isVisible === nextState.isVisible && prevState.height === nextState.height ? prevState : nextState
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
function resetKeyboardState() {
|
|
581
|
+
focusedElementRef.current = null;
|
|
582
|
+
setKeyboardInsetHeightEnv(0);
|
|
583
|
+
setKeyboardState({ isVisible: false, height: 0 });
|
|
584
|
+
}
|
|
585
|
+
if (!isEnabled) {
|
|
586
|
+
resetKeyboardState();
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
function updateKeyboardState() {
|
|
590
|
+
if (debounceTimer.current) {
|
|
591
|
+
clearTimeout(debounceTimer.current);
|
|
592
|
+
}
|
|
593
|
+
debounceTimer.current = setTimeout(() => {
|
|
594
|
+
const active = getActiveElement() ?? focusedElementRef.current;
|
|
595
|
+
const inputIsFocused = active ? willOpenKeyboard(active) : false;
|
|
596
|
+
if (!inputIsFocused) {
|
|
597
|
+
resetKeyboardState();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
focusedElementRef.current = active;
|
|
601
|
+
if (vk == null ? void 0 : vk.overlaysContent) {
|
|
602
|
+
const keyboardHeight = vk.boundingRect.height;
|
|
603
|
+
setKeyboardInsetHeightEnv(keyboardHeight);
|
|
604
|
+
setKeyboardState({ isVisible: true, height: keyboardHeight });
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (vv) {
|
|
608
|
+
const heightDiff = window.innerHeight - vv.height;
|
|
609
|
+
if (heightDiff > visualViewportThreshold) {
|
|
610
|
+
setKeyboardInsetHeightEnv(heightDiff);
|
|
611
|
+
setKeyboardState({ isVisible: true, height: heightDiff });
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
resetKeyboardState();
|
|
616
|
+
}, debounceDelay);
|
|
617
|
+
}
|
|
618
|
+
function handleFocusIn(e) {
|
|
619
|
+
if (e.target instanceof HTMLElement && willOpenKeyboard(e.target)) {
|
|
620
|
+
focusedElementRef.current = e.target;
|
|
621
|
+
updateKeyboardState();
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function handleFocusOut() {
|
|
625
|
+
requestAnimationFrame(() => {
|
|
626
|
+
focusedElementRef.current = getActiveElement();
|
|
627
|
+
updateKeyboardState();
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
631
|
+
document.addEventListener("focusout", handleFocusOut);
|
|
632
|
+
if (vv) {
|
|
633
|
+
vv.addEventListener("resize", updateKeyboardState);
|
|
634
|
+
vv.addEventListener("scroll", updateKeyboardState);
|
|
635
|
+
}
|
|
636
|
+
if (vk) {
|
|
637
|
+
if (virtualKeyboardOverlayUsers === 0) {
|
|
638
|
+
initialVirtualKeyboardOverlaysContent = vk.overlaysContent;
|
|
639
|
+
vk.overlaysContent = true;
|
|
640
|
+
}
|
|
641
|
+
virtualKeyboardOverlayUsers++;
|
|
642
|
+
vk.addEventListener("geometrychange", updateKeyboardState);
|
|
643
|
+
}
|
|
644
|
+
focusedElementRef.current = getActiveElement();
|
|
645
|
+
updateKeyboardState();
|
|
646
|
+
return () => {
|
|
647
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
648
|
+
document.removeEventListener("focusout", handleFocusOut);
|
|
649
|
+
if (vv) {
|
|
650
|
+
vv.removeEventListener("resize", updateKeyboardState);
|
|
651
|
+
vv.removeEventListener("scroll", updateKeyboardState);
|
|
652
|
+
}
|
|
653
|
+
if (vk) {
|
|
654
|
+
vk.removeEventListener("geometrychange", updateKeyboardState);
|
|
655
|
+
virtualKeyboardOverlayUsers = Math.max(
|
|
656
|
+
0,
|
|
657
|
+
virtualKeyboardOverlayUsers - 1
|
|
658
|
+
);
|
|
659
|
+
if (virtualKeyboardOverlayUsers === 0) {
|
|
660
|
+
vk.overlaysContent = initialVirtualKeyboardOverlaysContent ?? false;
|
|
661
|
+
initialVirtualKeyboardOverlaysContent = null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (debounceTimer.current) {
|
|
665
|
+
clearTimeout(debounceTimer.current);
|
|
666
|
+
}
|
|
667
|
+
resetKeyboardState();
|
|
668
|
+
};
|
|
669
|
+
}, [debounceDelay, isEnabled, visualViewportThreshold]);
|
|
670
|
+
return {
|
|
671
|
+
keyboardHeight: state.height,
|
|
672
|
+
isKeyboardOpen: state.isVisible
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function getVirtualKeyboardApi() {
|
|
676
|
+
return isHTTPS() && "virtualKeyboard" in navigator ? navigator.virtualKeyboard : null;
|
|
677
|
+
}
|
|
678
|
+
function getActiveElement() {
|
|
679
|
+
var _a;
|
|
680
|
+
let activeElement = document.activeElement;
|
|
681
|
+
while (activeElement instanceof HTMLElement && ((_a = activeElement.shadowRoot) == null ? void 0 : _a.activeElement)) {
|
|
682
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
683
|
+
}
|
|
684
|
+
return activeElement && willOpenKeyboard(activeElement) ? activeElement : null;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// src/hooks/use-keyboard-avoidance.ts
|
|
688
|
+
function useKeyboardAvoidance({
|
|
689
|
+
isEnabled,
|
|
690
|
+
containerRef,
|
|
691
|
+
onWillOpenKeyboard,
|
|
692
|
+
onDidOpenKeyboard
|
|
693
|
+
}) {
|
|
694
|
+
const [focusedElement, setFocusedElement] = React4.useState(
|
|
695
|
+
null
|
|
696
|
+
);
|
|
697
|
+
const keyboard = useVirtualKeyboard({
|
|
698
|
+
isEnabled,
|
|
699
|
+
containerRef
|
|
700
|
+
});
|
|
701
|
+
const handleFocusIn = useStableCallback(async (event) => {
|
|
702
|
+
var _a;
|
|
703
|
+
const element = event.target;
|
|
704
|
+
if (willOpenKeyboard(element) && ((_a = containerRef.current) == null ? void 0 : _a.contains(element))) {
|
|
705
|
+
await (onWillOpenKeyboard == null ? void 0 : onWillOpenKeyboard(event));
|
|
706
|
+
setFocusedElement(element);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
const handleFocusOut = useStableCallback((event) => {
|
|
710
|
+
const element = event.target;
|
|
711
|
+
if (focusedElement === element) {
|
|
712
|
+
setFocusedElement(null);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
React4.useEffect(() => {
|
|
716
|
+
if (!isEnabled) return;
|
|
717
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
718
|
+
document.addEventListener("focusout", handleFocusOut);
|
|
719
|
+
return () => {
|
|
720
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
721
|
+
document.removeEventListener("focusout", handleFocusOut);
|
|
722
|
+
};
|
|
723
|
+
}, [isEnabled]);
|
|
724
|
+
React4.useEffect(() => {
|
|
725
|
+
const containerElement = containerRef.current;
|
|
726
|
+
if (!isEnabled || !focusedElement || !containerElement || !keyboard.isKeyboardOpen) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
requestAnimationFrame(() => {
|
|
730
|
+
onDidOpenKeyboard == null ? void 0 : onDidOpenKeyboard(focusedElement);
|
|
731
|
+
});
|
|
732
|
+
}, [isEnabled, keyboard.isKeyboardOpen, focusedElement]);
|
|
733
|
+
return keyboard;
|
|
734
|
+
}
|
|
526
735
|
function useSafeAreaInsets() {
|
|
527
736
|
const [insets] = React4.useState(() => {
|
|
528
737
|
const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
@@ -665,13 +874,13 @@ function isScrollable(node, checkForOverflow) {
|
|
|
665
874
|
return false;
|
|
666
875
|
}
|
|
667
876
|
const style = window.getComputedStyle(node);
|
|
668
|
-
let
|
|
877
|
+
let scrollable = /(auto|scroll)/.test(
|
|
669
878
|
style.overflow + style.overflowX + style.overflowY
|
|
670
879
|
);
|
|
671
|
-
if (
|
|
672
|
-
|
|
880
|
+
if (scrollable && checkForOverflow) {
|
|
881
|
+
scrollable = node.scrollHeight !== node.clientHeight || node.scrollWidth !== node.clientWidth;
|
|
673
882
|
}
|
|
674
|
-
return
|
|
883
|
+
return scrollable;
|
|
675
884
|
}
|
|
676
885
|
function getScrollParent(node, checkForOverflow) {
|
|
677
886
|
let scrollableNode = node;
|
|
@@ -683,17 +892,6 @@ function getScrollParent(node, checkForOverflow) {
|
|
|
683
892
|
}
|
|
684
893
|
return scrollableNode || document.scrollingElement || document.documentElement;
|
|
685
894
|
}
|
|
686
|
-
var nonTextInputTypes = /* @__PURE__ */ new Set([
|
|
687
|
-
"checkbox",
|
|
688
|
-
"radio",
|
|
689
|
-
"range",
|
|
690
|
-
"color",
|
|
691
|
-
"file",
|
|
692
|
-
"image",
|
|
693
|
-
"button",
|
|
694
|
-
"submit",
|
|
695
|
-
"reset"
|
|
696
|
-
]);
|
|
697
895
|
var preventScrollCount = 0;
|
|
698
896
|
var restore;
|
|
699
897
|
function usePreventScroll(options = {}) {
|
|
@@ -860,9 +1058,6 @@ function scrollIntoView(target) {
|
|
|
860
1058
|
target = scrollable.parentElement;
|
|
861
1059
|
}
|
|
862
1060
|
}
|
|
863
|
-
function willOpenKeyboard(target) {
|
|
864
|
-
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
865
|
-
}
|
|
866
1061
|
function useSheetState({
|
|
867
1062
|
isOpen,
|
|
868
1063
|
onClosed: _onClosed,
|
|
@@ -907,108 +1102,6 @@ function useSheetState({
|
|
|
907
1102
|
}, [state]);
|
|
908
1103
|
return state;
|
|
909
1104
|
}
|
|
910
|
-
function useVirtualKeyboard(options = {}) {
|
|
911
|
-
const {
|
|
912
|
-
containerRef,
|
|
913
|
-
isEnabled = true,
|
|
914
|
-
debounceDelay = 100,
|
|
915
|
-
includeContentEditable = true,
|
|
916
|
-
visualViewportThreshold = 100
|
|
917
|
-
} = options;
|
|
918
|
-
const [state, setState] = React4.useState({
|
|
919
|
-
isVisible: false,
|
|
920
|
-
height: 0
|
|
921
|
-
});
|
|
922
|
-
const focusedElementRef = React4.useRef(null);
|
|
923
|
-
const debounceTimer = React4.useRef(null);
|
|
924
|
-
const isTextInput = useStableCallback((el) => {
|
|
925
|
-
return (el == null ? void 0 : el.tagName) === "INPUT" || (el == null ? void 0 : el.tagName) === "TEXTAREA" || includeContentEditable && el instanceof HTMLElement && el.isContentEditable;
|
|
926
|
-
});
|
|
927
|
-
React4.useEffect(() => {
|
|
928
|
-
if (!isEnabled) return;
|
|
929
|
-
const vv = window.visualViewport;
|
|
930
|
-
const vk = navigator.virtualKeyboard;
|
|
931
|
-
function setKeyboardInsetHeightEnv(height) {
|
|
932
|
-
const element = (containerRef == null ? void 0 : containerRef.current) || document.documentElement;
|
|
933
|
-
if (window.isSecureContext) {
|
|
934
|
-
element.style.setProperty(
|
|
935
|
-
"--keyboard-inset-height",
|
|
936
|
-
`env(keyboard-inset-height, ${height}px)`
|
|
937
|
-
);
|
|
938
|
-
} else {
|
|
939
|
-
element.style.setProperty("--keyboard-inset-height", `${height}px`);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
function handleFocusIn(e) {
|
|
943
|
-
if (e.target instanceof HTMLElement && isTextInput(e.target)) {
|
|
944
|
-
focusedElementRef.current = e.target;
|
|
945
|
-
updateKeyboardState();
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
function handleFocusOut() {
|
|
949
|
-
focusedElementRef.current = null;
|
|
950
|
-
updateKeyboardState();
|
|
951
|
-
}
|
|
952
|
-
function updateKeyboardState() {
|
|
953
|
-
if (debounceTimer.current) {
|
|
954
|
-
clearTimeout(debounceTimer.current);
|
|
955
|
-
}
|
|
956
|
-
debounceTimer.current = setTimeout(() => {
|
|
957
|
-
const active = focusedElementRef.current;
|
|
958
|
-
const inputIsFocused = isTextInput(active);
|
|
959
|
-
if (!inputIsFocused) {
|
|
960
|
-
setKeyboardInsetHeightEnv(0);
|
|
961
|
-
setState({ isVisible: false, height: 0 });
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
if (vv) {
|
|
965
|
-
const heightDiff = window.innerHeight - vv.height;
|
|
966
|
-
if (heightDiff > visualViewportThreshold) {
|
|
967
|
-
setKeyboardInsetHeightEnv(heightDiff);
|
|
968
|
-
setState({ isVisible: true, height: heightDiff });
|
|
969
|
-
} else {
|
|
970
|
-
setKeyboardInsetHeightEnv(0);
|
|
971
|
-
setState({ isVisible: false, height: 0 });
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
}, debounceDelay);
|
|
975
|
-
}
|
|
976
|
-
window.addEventListener("focusin", handleFocusIn);
|
|
977
|
-
window.addEventListener("focusout", handleFocusOut);
|
|
978
|
-
if (vv) {
|
|
979
|
-
vv.addEventListener("resize", updateKeyboardState);
|
|
980
|
-
vv.addEventListener("scroll", updateKeyboardState);
|
|
981
|
-
}
|
|
982
|
-
let currentOverlaysContent = false;
|
|
983
|
-
if (vk) {
|
|
984
|
-
currentOverlaysContent = vk.overlaysContent;
|
|
985
|
-
vk.overlaysContent = true;
|
|
986
|
-
}
|
|
987
|
-
return () => {
|
|
988
|
-
window.removeEventListener("focusin", handleFocusIn);
|
|
989
|
-
window.removeEventListener("focusout", handleFocusOut);
|
|
990
|
-
if (vv) {
|
|
991
|
-
vv.removeEventListener("resize", updateKeyboardState);
|
|
992
|
-
vv.removeEventListener("scroll", updateKeyboardState);
|
|
993
|
-
}
|
|
994
|
-
if (vk) {
|
|
995
|
-
vk.overlaysContent = currentOverlaysContent;
|
|
996
|
-
}
|
|
997
|
-
if (debounceTimer.current) {
|
|
998
|
-
clearTimeout(debounceTimer.current);
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
}, [
|
|
1002
|
-
debounceDelay,
|
|
1003
|
-
includeContentEditable,
|
|
1004
|
-
isEnabled,
|
|
1005
|
-
visualViewportThreshold
|
|
1006
|
-
]);
|
|
1007
|
-
return {
|
|
1008
|
-
keyboardHeight: state.height,
|
|
1009
|
-
isKeyboardOpen: state.isVisible
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
1105
|
|
|
1013
1106
|
// src/snap.ts
|
|
1014
1107
|
function computeSnapPoints({
|
|
@@ -1183,11 +1276,6 @@ var Sheet = React4.forwardRef(
|
|
|
1183
1276
|
type: "tween",
|
|
1184
1277
|
...reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig
|
|
1185
1278
|
};
|
|
1186
|
-
const keyboard = useVirtualKeyboard({
|
|
1187
|
-
isEnabled: isOpen && avoidKeyboard,
|
|
1188
|
-
containerRef: sheetRef
|
|
1189
|
-
});
|
|
1190
|
-
const disableDrag = keyboard.isKeyboardOpen || disableDragProp;
|
|
1191
1279
|
const zIndex = react.useTransform(
|
|
1192
1280
|
y,
|
|
1193
1281
|
(val) => val + 2 >= closedY ? -1 : (style == null ? void 0 : style.zIndex) ?? 9999
|
|
@@ -1231,14 +1319,45 @@ var Sheet = React4.forwardRef(
|
|
|
1231
1319
|
onComplete: () => updateSnap(snapIndex)
|
|
1232
1320
|
});
|
|
1233
1321
|
});
|
|
1322
|
+
const keyboard = useKeyboardAvoidance({
|
|
1323
|
+
isEnabled: isOpen && avoidKeyboard,
|
|
1324
|
+
containerRef: sheetRef,
|
|
1325
|
+
onWillOpenKeyboard: async () => {
|
|
1326
|
+
const lastSnapPoint = snapPoints[snapPoints.length - 1];
|
|
1327
|
+
if (lastSnapPoint && lastSnapPoint.snapIndex !== currentSnap) {
|
|
1328
|
+
await react.animate(y, lastSnapPoint.snapValueY, animationOptions);
|
|
1329
|
+
updateSnap(lastSnapPoint.snapIndex);
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
onDidOpenKeyboard: (focusedElement) => {
|
|
1333
|
+
const sheetElement = sheetRef.current;
|
|
1334
|
+
if (!sheetElement) return;
|
|
1335
|
+
const inputRect = focusedElement.getBoundingClientRect();
|
|
1336
|
+
const containerRect = sheetElement.getBoundingClientRect();
|
|
1337
|
+
const scroller = sheetElement.querySelector(
|
|
1338
|
+
".react-modal-sheet-content-scroller"
|
|
1339
|
+
);
|
|
1340
|
+
const scrollTarget = Math.max(
|
|
1341
|
+
inputRect.top - containerRect.top + scroller.scrollTop - inputRect.height,
|
|
1342
|
+
0
|
|
1343
|
+
);
|
|
1344
|
+
requestAnimationFrame(() => {
|
|
1345
|
+
scroller.scrollTo({ top: scrollTarget, behavior: "smooth" });
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
const disableDrag = keyboard.isKeyboardOpen || disableDragProp;
|
|
1234
1350
|
const blurActiveInput = useStableCallback(() => {
|
|
1351
|
+
var _a;
|
|
1235
1352
|
const focusedElement = document.activeElement;
|
|
1236
|
-
if (
|
|
1237
|
-
const isInput = focusedElement.tagName === "INPUT" || focusedElement.tagName === "TEXTAREA";
|
|
1238
|
-
if (isInput && sheetRef.current.contains(focusedElement)) {
|
|
1353
|
+
if (focusedElement && willOpenKeyboard(focusedElement) && ((_a = sheetRef.current) == null ? void 0 : _a.contains(focusedElement))) {
|
|
1239
1354
|
focusedElement.blur();
|
|
1240
1355
|
}
|
|
1241
1356
|
});
|
|
1357
|
+
const onDragStart = useStableCallback((event, info) => {
|
|
1358
|
+
blurActiveInput();
|
|
1359
|
+
onDragStartProp == null ? void 0 : onDragStartProp(event, info);
|
|
1360
|
+
});
|
|
1242
1361
|
const onDrag = useStableCallback((event, info) => {
|
|
1243
1362
|
onDragProp == null ? void 0 : onDragProp(event, info);
|
|
1244
1363
|
const currentY = y.get();
|
|
@@ -1247,10 +1366,6 @@ var Sheet = React4.forwardRef(
|
|
|
1247
1366
|
if (velocity < 0) indicatorRotation.set(-10);
|
|
1248
1367
|
y.set(Math.max(currentY + info.delta.y, 0));
|
|
1249
1368
|
});
|
|
1250
|
-
const onDragStart = useStableCallback((event, info) => {
|
|
1251
|
-
blurActiveInput();
|
|
1252
|
-
onDragStartProp == null ? void 0 : onDragStartProp(event, info);
|
|
1253
|
-
});
|
|
1254
1369
|
const onDragEnd = useStableCallback((event, info) => {
|
|
1255
1370
|
blurActiveInput();
|
|
1256
1371
|
onDragEndProp == null ? void 0 : onDragEndProp(event, info);
|
|
@@ -1307,29 +1422,6 @@ var Sheet = React4.forwardRef(
|
|
|
1307
1422
|
height: sheetHeight,
|
|
1308
1423
|
snapTo
|
|
1309
1424
|
}));
|
|
1310
|
-
React4.useEffect(() => {
|
|
1311
|
-
if (isOpen && snapPoints.length > 0 && avoidKeyboard && keyboard.isKeyboardOpen) {
|
|
1312
|
-
const focusedElement = document.activeElement;
|
|
1313
|
-
const visibleSheetHeight = yInverted.get() - keyboard.keyboardHeight;
|
|
1314
|
-
if (visibleSheetHeight <= 0 && focusedElement) {
|
|
1315
|
-
const currentY = y.get();
|
|
1316
|
-
const diffY = Math.max(currentY - Math.abs(visibleSheetHeight), 0);
|
|
1317
|
-
requestAnimationFrame(() => {
|
|
1318
|
-
var _a;
|
|
1319
|
-
const inputRect = focusedElement.getBoundingClientRect();
|
|
1320
|
-
const sheetRect = (_a = sheetRef.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
1321
|
-
let inputOffset = 0;
|
|
1322
|
-
if (sheetRect) {
|
|
1323
|
-
inputOffset = inputRect.top - sheetRect.top + inputRect.height;
|
|
1324
|
-
}
|
|
1325
|
-
react.animate(y, diffY - inputOffset, animationOptions);
|
|
1326
|
-
});
|
|
1327
|
-
return () => {
|
|
1328
|
-
react.animate(y, currentY, animationOptions);
|
|
1329
|
-
};
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
}, [isOpen, keyboard.isKeyboardOpen]);
|
|
1333
1425
|
useModalEffect({
|
|
1334
1426
|
y,
|
|
1335
1427
|
detent,
|
|
@@ -1376,6 +1468,7 @@ var Sheet = React4.forwardRef(
|
|
|
1376
1468
|
dragProps,
|
|
1377
1469
|
indicatorRotation,
|
|
1378
1470
|
avoidKeyboard,
|
|
1471
|
+
prefersReducedMotion,
|
|
1379
1472
|
sheetBoundsRef,
|
|
1380
1473
|
sheetRef,
|
|
1381
1474
|
unstyled,
|