react-modal-sheet 5.3.1 → 5.4.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 +9 -6
- package/dist/index.js +73 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/SheetBackdrop.tsx +10 -4
- package/src/debug.ts +3 -2
- package/src/hooks/use-focused-input.ts +46 -0
- package/src/sheet.tsx +63 -49
- package/src/types.tsx +1 -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.js
CHANGED
|
@@ -218,13 +218,16 @@ var SheetBackdrop = React4.forwardRef(
|
|
|
218
218
|
...style,
|
|
219
219
|
pointerEvents
|
|
220
220
|
};
|
|
221
|
+
const animationProps = sheetContext.prefersReducedMotion ? {} : {
|
|
222
|
+
initial: { opacity: 0 },
|
|
223
|
+
animate: { opacity: 1 },
|
|
224
|
+
exit: { opacity: 0 },
|
|
225
|
+
transition: { duration: 1 }
|
|
226
|
+
};
|
|
221
227
|
return /* @__PURE__ */ React4__default.default.createElement(
|
|
222
228
|
Comp,
|
|
223
229
|
{
|
|
224
|
-
|
|
225
|
-
animate: { opacity: 1 },
|
|
226
|
-
exit: { opacity: 0 },
|
|
227
|
-
transition: { duration: 1 },
|
|
230
|
+
...animationProps,
|
|
228
231
|
...rest,
|
|
229
232
|
ref,
|
|
230
233
|
className: `react-modal-sheet-backdrop ${className}`,
|
|
@@ -523,6 +526,36 @@ function useDimensions() {
|
|
|
523
526
|
}, []);
|
|
524
527
|
return dimensions;
|
|
525
528
|
}
|
|
529
|
+
function useFocusedInput({
|
|
530
|
+
isEnabled,
|
|
531
|
+
sheetRef
|
|
532
|
+
}) {
|
|
533
|
+
const [focusedInput, setFocusedInput] = React4.useState(null);
|
|
534
|
+
React4.useEffect(() => {
|
|
535
|
+
if (!isEnabled) return;
|
|
536
|
+
const handleFocusIn = (event) => {
|
|
537
|
+
var _a;
|
|
538
|
+
const focusedElement = event.target;
|
|
539
|
+
const isInput = (focusedElement == null ? void 0 : focusedElement.tagName) === "INPUT" || (focusedElement == null ? void 0 : focusedElement.tagName) === "TEXTAREA";
|
|
540
|
+
if (isInput && ((_a = sheetRef.current) == null ? void 0 : _a.contains(focusedElement))) {
|
|
541
|
+
setFocusedInput(focusedElement);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
const handleFocusOut = (event) => {
|
|
545
|
+
const focusedElement = event.target;
|
|
546
|
+
if (focusedElement === focusedInput) {
|
|
547
|
+
setFocusedInput(null);
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
551
|
+
document.addEventListener("focusout", handleFocusOut);
|
|
552
|
+
return () => {
|
|
553
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
554
|
+
document.removeEventListener("focusout", handleFocusOut);
|
|
555
|
+
};
|
|
556
|
+
}, [isEnabled, focusedInput]);
|
|
557
|
+
return focusedInput;
|
|
558
|
+
}
|
|
526
559
|
function useSafeAreaInsets() {
|
|
527
560
|
const [insets] = React4.useState(() => {
|
|
528
561
|
const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
@@ -1307,29 +1340,6 @@ var Sheet = React4.forwardRef(
|
|
|
1307
1340
|
height: sheetHeight,
|
|
1308
1341
|
snapTo
|
|
1309
1342
|
}));
|
|
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
1343
|
useModalEffect({
|
|
1334
1344
|
y,
|
|
1335
1345
|
detent,
|
|
@@ -1341,6 +1351,41 @@ var Sheet = React4.forwardRef(
|
|
|
1341
1351
|
usePreventScroll({
|
|
1342
1352
|
isDisabled: disableScrollLocking || !isOpen
|
|
1343
1353
|
});
|
|
1354
|
+
const focusedInput = useFocusedInput({
|
|
1355
|
+
isEnabled: isOpen && avoidKeyboard,
|
|
1356
|
+
sheetRef
|
|
1357
|
+
});
|
|
1358
|
+
React4.useEffect(() => {
|
|
1359
|
+
if (!isOpen || !avoidKeyboard) {
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
async function adjustSheetForKeyboard() {
|
|
1363
|
+
const sheetElement = sheetRef.current;
|
|
1364
|
+
if (!sheetElement || !focusedInput || !keyboard.isKeyboardOpen) {
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
const lastSnapPoint = snapPoints[snapPoints.length - 1];
|
|
1368
|
+
if (lastSnapPoint) {
|
|
1369
|
+
await react.animate(y, lastSnapPoint.snapValueY, animationOptions);
|
|
1370
|
+
updateSnap(lastSnapPoint.snapIndex);
|
|
1371
|
+
}
|
|
1372
|
+
requestAnimationFrame(() => {
|
|
1373
|
+
const inputRect = focusedInput.getBoundingClientRect();
|
|
1374
|
+
const sheetRect = sheetElement.getBoundingClientRect();
|
|
1375
|
+
const scroller = sheetElement.querySelector(
|
|
1376
|
+
".react-modal-sheet-content-scroller"
|
|
1377
|
+
);
|
|
1378
|
+
const scrollTarget = Math.max(
|
|
1379
|
+
inputRect.top - sheetRect.top + scroller.scrollTop - inputRect.height,
|
|
1380
|
+
0
|
|
1381
|
+
);
|
|
1382
|
+
requestAnimationFrame(() => {
|
|
1383
|
+
scroller.scrollTo({ top: scrollTarget, behavior: "smooth" });
|
|
1384
|
+
});
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
adjustSheetForKeyboard();
|
|
1388
|
+
}, [isOpen, avoidKeyboard, keyboard.isKeyboardOpen, focusedInput]);
|
|
1344
1389
|
const state = useSheetState({
|
|
1345
1390
|
isOpen,
|
|
1346
1391
|
onOpen: async () => {
|
|
@@ -1376,6 +1421,7 @@ var Sheet = React4.forwardRef(
|
|
|
1376
1421
|
dragProps,
|
|
1377
1422
|
indicatorRotation,
|
|
1378
1423
|
avoidKeyboard,
|
|
1424
|
+
prefersReducedMotion,
|
|
1379
1425
|
sheetBoundsRef,
|
|
1380
1426
|
sheetRef,
|
|
1381
1427
|
unstyled,
|