refactor: enhance mobile swipe gesture handling and improve outlet behavior on compact routes
This commit is contained in:
67
overlay/src/app/hooks/useMobileKeyboardLayout.ts
Normal file
67
overlay/src/app/hooks/useMobileKeyboardLayout.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const KEYBOARD_HEIGHT_THRESHOLD_PX = 120;
|
||||
|
||||
const getIsLandscape = (): boolean => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
if (window.matchMedia?.('(orientation: landscape)').matches) return true;
|
||||
return window.innerWidth > window.innerHeight;
|
||||
};
|
||||
|
||||
const getKeyboardOpen = (): boolean => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
|
||||
const viewport = window.visualViewport;
|
||||
if (viewport) {
|
||||
const obscured = window.innerHeight - viewport.height - viewport.offsetTop;
|
||||
if (obscured > KEYBOARD_HEIGHT_THRESHOLD_PX) return true;
|
||||
}
|
||||
|
||||
const active = document.activeElement;
|
||||
if (!(active instanceof HTMLElement)) return false;
|
||||
if (active.isContentEditable) return true;
|
||||
const tag = active.tagName;
|
||||
return tag === 'INPUT' || tag === 'TEXTAREA';
|
||||
};
|
||||
|
||||
export type MobileKeyboardLayout = {
|
||||
isLandscape: boolean;
|
||||
keyboardOpen: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Landscape + soft-keyboard layout signals for compact room chrome.
|
||||
* Uses visualViewport when available (Capacitor/Android WebView).
|
||||
*/
|
||||
export const useMobileKeyboardLayout = (): MobileKeyboardLayout => {
|
||||
const [isLandscape, setIsLandscape] = useState(getIsLandscape);
|
||||
const [keyboardOpen, setKeyboardOpen] = useState(getKeyboardOpen);
|
||||
|
||||
useEffect(() => {
|
||||
const sync = () => {
|
||||
setIsLandscape(getIsLandscape());
|
||||
setKeyboardOpen(getKeyboardOpen());
|
||||
};
|
||||
|
||||
sync();
|
||||
|
||||
const orientationQuery = window.matchMedia?.('(orientation: landscape)');
|
||||
orientationQuery?.addEventListener?.('change', sync);
|
||||
window.addEventListener('resize', sync);
|
||||
window.visualViewport?.addEventListener('resize', sync);
|
||||
window.visualViewport?.addEventListener('scroll', sync);
|
||||
window.addEventListener('focusin', sync);
|
||||
window.addEventListener('focusout', sync);
|
||||
|
||||
return () => {
|
||||
orientationQuery?.removeEventListener?.('change', sync);
|
||||
window.removeEventListener('resize', sync);
|
||||
window.visualViewport?.removeEventListener('resize', sync);
|
||||
window.visualViewport?.removeEventListener('scroll', sync);
|
||||
window.removeEventListener('focusin', sync);
|
||||
window.removeEventListener('focusout', sync);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { isLandscape, keyboardOpen };
|
||||
};
|
||||
Reference in New Issue
Block a user