import { useEffect } from 'react' import scrollIntoView from 'scroll-into-view-if-needed' // 全局兜底: 输入框获得焦点后保证它滚进可视区。 // // 原生层 (scripts/ci/android-softinput.sh) 已经把软键盘高度从 WebView 里扣掉, // 弹层 (position: fixed; bottom: 0) 会自动上移; 这里只负责把被聚焦的控件 // 滚到可视区中间, 覆盖页面滚动、Popup 内部滚动等所有场景。 // // 用 scroll-into-view-if-needed (成熟库) 而不是手写滚动计算: // 它会自动处理嵌套滚动容器, 并在目标已可见时不做多余滚动。 const FIELD_SELECTOR = 'input, textarea, [contenteditable="true"]' function isField(el) { return !!el && typeof el.matches === 'function' && el.matches(FIELD_SELECTOR) } export default function useKeepInputVisible() { useEffect(() => { let timer = 0 const reveal = (el, behavior) => { if (!isField(el)) return scrollIntoView(el, { behavior, block: 'center', inline: 'nearest', scrollMode: 'if-needed', }) } const onFocusIn = (e) => { const el = e.target if (!isField(el)) return // 先滚一次 (部分机型聚焦后立刻可用), 等键盘动画结束后再校正一次 reveal(el, 'smooth') clearTimeout(timer) timer = setTimeout(() => reveal(el, 'smooth'), 260) } // 键盘弹出/收起会改变可视区高度 (原生层已让 WebView 变矮) const onViewportResize = () => { const el = document.activeElement if (!isField(el)) return reveal(el, 'auto') } document.addEventListener('focusin', onFocusIn) window.addEventListener('resize', onViewportResize) window.visualViewport?.addEventListener('resize', onViewportResize) return () => { clearTimeout(timer) document.removeEventListener('focusin', onFocusIn) window.removeEventListener('resize', onViewportResize) window.visualViewport?.removeEventListener('resize', onViewportResize) } }, []) }