mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-15 08:48:15 +00:00
fix: Prevent scrolling behind menus and modals in Safari iOS (#35183)
This commit is contained in:
parent
dbb20f76a7
commit
c1ef1f62d5
|
@ -1,71 +1,30 @@
|
|||
import { useLayoutEffect, useEffect, useState } from 'react';
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
import { createAppSelector, useAppSelector } from 'mastodon/store';
|
||||
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
|
||||
|
||||
const getShouldLockBodyScroll = createAppSelector(
|
||||
[
|
||||
(state) => state.navigation.open,
|
||||
(state) => state.modal.get('stack').size > 0,
|
||||
],
|
||||
(isMobileMenuOpen: boolean, isModalOpen: boolean) => {
|
||||
return isMobileMenuOpen || isModalOpen;
|
||||
},
|
||||
(isMobileMenuOpen: boolean, isModalOpen: boolean) =>
|
||||
isMobileMenuOpen || isModalOpen,
|
||||
);
|
||||
|
||||
/**
|
||||
* This component locks scrolling on the `body` element when
|
||||
* This component locks scrolling on the body when
|
||||
* `getShouldLockBodyScroll` returns true.
|
||||
*
|
||||
* The scrollbar width is taken into account and written to
|
||||
* a CSS custom property `--root-scrollbar-width`
|
||||
*/
|
||||
|
||||
export const BodyScrollLock: React.FC = () => {
|
||||
const shouldLockBodyScroll = useAppSelector(getShouldLockBodyScroll);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
document.body.classList.toggle('with-modals--active', shouldLockBodyScroll);
|
||||
document.documentElement.classList.toggle(
|
||||
'has-modal',
|
||||
shouldLockBodyScroll,
|
||||
);
|
||||
}, [shouldLockBodyScroll]);
|
||||
|
||||
const [scrollbarWidth, setScrollbarWidth] = useState(() =>
|
||||
getScrollbarWidth(),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setScrollbarWidth(getScrollbarWidth());
|
||||
};
|
||||
window.addEventListener('resize', handleResize, { passive: true });
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Inject style element to make scrollbar width available
|
||||
// as CSS custom property
|
||||
useLayoutEffect(() => {
|
||||
const nonce = document
|
||||
.querySelector('meta[name=style-nonce]')
|
||||
?.getAttribute('content');
|
||||
|
||||
if (nonce) {
|
||||
const styleEl = document.createElement('style');
|
||||
styleEl.nonce = nonce;
|
||||
styleEl.innerHTML = `
|
||||
:root {
|
||||
--root-scrollbar-width: ${scrollbarWidth}px;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(styleEl);
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(styleEl);
|
||||
};
|
||||
}
|
||||
|
||||
return () => '';
|
||||
}, [scrollbarWidth]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import { isMobile } from '../is_mobile';
|
||||
|
||||
export const getScrollbarWidth = () => {
|
||||
if (isMobile(window.innerWidth)) {
|
||||
return 0;
|
||||
}
|
||||
const outer = document.createElement('div');
|
||||
outer.style.visibility = 'hidden';
|
||||
outer.style.overflow = 'scroll';
|
||||
document.body.appendChild(outer);
|
||||
|
||||
const inner = document.createElement('div');
|
||||
outer.appendChild(inner);
|
||||
|
||||
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
||||
outer.remove();
|
||||
|
||||
return scrollbarWidth;
|
||||
};
|
|
@ -1,6 +1,20 @@
|
|||
@use 'variables' as *;
|
||||
@use 'functions' as *;
|
||||
|
||||
html.has-modal {
|
||||
&,
|
||||
body {
|
||||
touch-action: none;
|
||||
overscroll-behavior: none;
|
||||
-webkit-overflow-scrolling: auto;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
background: var(--background-color);
|
||||
|
@ -64,21 +78,6 @@ body {
|
|||
height: 100%;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
&.with-modals--active {
|
||||
overflow-y: hidden;
|
||||
overscroll-behavior: none;
|
||||
margin-right: var(--root-scrollbar-width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
&.with-modals {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
&--active {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.player {
|
||||
|
|
|
@ -2896,10 +2896,6 @@ a.account__display-name {
|
|||
border-top: 1px solid var(--background-border-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
.with-modals--active & {
|
||||
padding-right: var(--root-scrollbar-width);
|
||||
}
|
||||
|
||||
.layout-multiple-columns & {
|
||||
display: none;
|
||||
}
|
||||
|
@ -3170,7 +3166,7 @@ a.account__display-name {
|
|||
.navigation-panel {
|
||||
margin: 0;
|
||||
border-inline-start: 1px solid var(--background-border-color);
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.navigation-panel__banner,
|
||||
|
@ -3228,6 +3224,7 @@ a.account__display-name {
|
|||
.navigation-panel {
|
||||
width: 284px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&__menu {
|
||||
flex-shrink: 0;
|
||||
|
|
Loading…
Reference in New Issue
Block a user