This commit is contained in:
weretere 2026-04-29 16:39:51 +01:00 committed by GitHub
commit b3390912cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 15 deletions

View file

@ -30,12 +30,6 @@
overflow: hidden !important; overflow: hidden !important;
} }
.modalOpenIOS {
position: fixed;
right: 0;
left: 0;
}
/* /*
* Sizes * Sizes
*/ */

View file

@ -10,7 +10,6 @@ interface CssExports {
'modalBackdrop': string; 'modalBackdrop': string;
'modalContainer': string; 'modalContainer': string;
'modalOpen': string; 'modalOpen': string;
'modalOpenIOS': string;
'small': string; 'small': string;
} }
export const cssExports: CssExports; export const cssExports: CssExports;

View file

@ -12,7 +12,7 @@ import FocusLock from 'react-focus-lock';
import ErrorBoundary from 'Components/Error/ErrorBoundary'; import ErrorBoundary from 'Components/Error/ErrorBoundary';
import usePrevious from 'Helpers/Hooks/usePrevious'; import usePrevious from 'Helpers/Hooks/usePrevious';
import { Size } from 'Helpers/Props/sizes'; import { Size } from 'Helpers/Props/sizes';
import { isIOS } from 'Utilities/browser'; import { isMobile } from 'Utilities/browser';
import * as keyCodes from 'Utilities/Constants/keyCodes'; import * as keyCodes from 'Utilities/Constants/keyCodes';
import { setScrollLock } from 'Utilities/scrollLock'; import { setScrollLock } from 'Utilities/scrollLock';
import ModalError from './ModalError'; import ModalError from './ModalError';
@ -29,6 +29,21 @@ function removeFromOpenModals(id: string) {
} }
} }
// Mobile scroll lock: block touchmove outside the modal portal. Avoids
// mutating body position/overflow — doing so resets window.scrollY to 0,
// which causes react-virtualized WindowScroller to unmount the row that
// owns the just-opened modal (Discover page regression).
function preventTouchScroll(event: TouchEvent) {
let target = event.target as HTMLElement | null;
while (target) {
if (target.id === 'portal-root') {
return;
}
target = target.parentElement;
}
event.preventDefault();
}
function findEventTarget(event: TouchEvent | MouseEvent) { function findEventTarget(event: TouchEvent | MouseEvent) {
if ('changedTouches' in event) { if ('changedTouches' in event) {
const changedTouches = event.changedTouches; const changedTouches = event.changedTouches;
@ -70,7 +85,6 @@ function Modal({
}: ModalProps) { }: ModalProps) {
const backgroundRef = useRef<HTMLDivElement>(null); const backgroundRef = useRef<HTMLDivElement>(null);
const isBackdropPressed = useRef(false); const isBackdropPressed = useRef(false);
const bodyScrollTop = useRef(0);
const wasOpen = usePrevious(isOpen); const wasOpen = usePrevious(isOpen);
const modalId = useId(); const modalId = useId();
@ -125,10 +139,14 @@ function Modal({
openModals.push(modalId); openModals.push(modalId);
if (openModals.length === 1) { if (openModals.length === 1) {
if (isIOS()) { if (isMobile()) {
// Don't mutate body position/overflow on mobile — it resets
// window.scrollY which makes WindowScroller unmount this row's
// modal. Use a touchmove listener to block scroll instead.
setScrollLock(true); setScrollLock(true);
bodyScrollTop.current = document.body.scrollTop; document.addEventListener('touchmove', preventTouchScroll, {
elementClass(document.body).add(styles.modalOpenIOS); passive: false,
});
} else { } else {
elementClass(document.body).add(styles.modalOpen); elementClass(document.body).add(styles.modalOpen);
} }
@ -139,9 +157,8 @@ function Modal({
if (openModals.length === 0) { if (openModals.length === 0) {
setScrollLock(false); setScrollLock(false);
if (isIOS()) { if (isMobile()) {
elementClass(document.body).remove(styles.modalOpenIOS); document.removeEventListener('touchmove', preventTouchScroll);
document.body.scrollTop = bodyScrollTop.current;
} else { } else {
elementClass(document.body).remove(styles.modalOpen); elementClass(document.body).remove(styles.modalOpen);
} }