fix(compare): keep the add-school sheet above the on-screen keyboard
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m6s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 47s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 1m31s
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m6s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 47s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 1m31s
The bottom-sheet overlay was position:fixed inset:0, sized to the LAYOUT viewport, so when the mobile keyboard opened it kept full height: the sheet (anchored to the overlay's bottom) and the lower half of the dim backdrop sat behind the keyboard, hiding the search input and results. Track window.visualViewport (height + offsetTop) while the modal is open and pin the overlay to the visible region, so backdrop and sheet stay above the keyboard. Size the sheet against the overlay (max-height:100%, min-height:min(55vh,100%)) instead of vh, and drop the results list's own inner scroll on mobile so the sheet is a single scroll area. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
'use client';
|
||||
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useEffect, useCallback, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import styles from './Modal.module.css';
|
||||
|
||||
@@ -18,6 +18,8 @@ interface ModalProps {
|
||||
}
|
||||
|
||||
export function Modal({ isOpen, onClose, children, title, size = 'medium' }: ModalProps) {
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleEscape = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
@@ -39,6 +41,32 @@ export function Modal({ isOpen, onClose, children, title, size = 'medium' }: Mod
|
||||
};
|
||||
}, [isOpen, handleEscape]);
|
||||
|
||||
// Pin the overlay to the VISUAL viewport, not the layout viewport. On mobile
|
||||
// the on-screen keyboard shrinks the visual viewport but not the layout one,
|
||||
// so a `position: fixed; inset: 0` overlay keeps full height — leaving the
|
||||
// bottom-anchored sheet (and the dim backdrop's lower half) hidden behind
|
||||
// the keyboard. Tracking visualViewport.height/offsetTop keeps the whole
|
||||
// overlay — backdrop and sheet — inside the visible area, above the keyboard.
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
const vv = typeof window !== 'undefined' ? window.visualViewport : null;
|
||||
const el = overlayRef.current;
|
||||
if (!vv || !el) return;
|
||||
|
||||
const sync = () => {
|
||||
el.style.top = `${vv.offsetTop}px`;
|
||||
el.style.height = `${vv.height}px`;
|
||||
el.style.bottom = 'auto';
|
||||
};
|
||||
sync();
|
||||
vv.addEventListener('resize', sync);
|
||||
vv.addEventListener('scroll', sync);
|
||||
return () => {
|
||||
vv.removeEventListener('resize', sync);
|
||||
vv.removeEventListener('scroll', sync);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
if (!isOpen || typeof window === 'undefined') return null;
|
||||
|
||||
const handleOverlayClick = (e: React.MouseEvent) => {
|
||||
@@ -48,7 +76,7 @@ export function Modal({ isOpen, onClose, children, title, size = 'medium' }: Mod
|
||||
};
|
||||
|
||||
return createPortal(
|
||||
<div className={styles.overlay} onClick={handleOverlayClick}>
|
||||
<div ref={overlayRef} className={styles.overlay} onClick={handleOverlayClick}>
|
||||
<div className={`${styles.modal} ${styles[size]}`}>
|
||||
<div className={styles.header}>
|
||||
{title && <h2 className={styles.title}>{title}</h2>}
|
||||
|
||||
Reference in New Issue
Block a user