fix: avoid iPad PWA traffic light overlap

Adds tablet standalone PWA detection
Reuses macOS traffic-light header spacing
Protects session sidebar and multirun headers
This commit is contained in:
Bohdan Triapitsyn
2026-05-03 21:39:30 +03:00
parent 9964dcdb99
commit fc2f4746e5
5 changed files with 83 additions and 11 deletions
+63
View File
@@ -141,6 +141,69 @@ export function isMobileDeviceViaCSS(): boolean {
return isMobileValue === '1' || isMobileValue === 'true';
}
export const isStandalonePwaRuntime = (): boolean => {
if (typeof window === 'undefined') return false;
const standaloneNavigator = navigator as Navigator & { standalone?: boolean };
return Boolean(
standaloneNavigator.standalone === true
|| window.matchMedia?.('(display-mode: standalone)')?.matches
|| window.matchMedia?.('(display-mode: fullscreen)')?.matches
);
};
export const isTabletStandalonePwaRuntime = (): boolean => {
if (typeof window === 'undefined' || isDesktopShell()) return false;
const maxTouchPoints = typeof navigator !== 'undefined' ? navigator.maxTouchPoints ?? 0 : 0;
return isStandalonePwaRuntime() && maxTouchPoints > 0 && window.innerWidth > BREAKPOINTS.md;
};
export function useTabletStandalonePwaRuntime(): boolean {
const [value, setValue] = React.useState<boolean>(() => isTabletStandalonePwaRuntime());
React.useEffect(() => {
if (typeof window === 'undefined') return;
const update = () => setValue(isTabletStandalonePwaRuntime());
const standaloneQuery = window.matchMedia?.('(display-mode: standalone)');
const fullscreenQuery = window.matchMedia?.('(display-mode: fullscreen)');
update();
window.addEventListener('resize', update);
window.addEventListener('focus', update);
const addQueryListener = (query: MediaQueryList | undefined) => {
if (!query) return;
if (typeof query.addEventListener === 'function') {
query.addEventListener('change', update);
} else if (typeof query.addListener === 'function') {
query.addListener(update);
}
};
const removeQueryListener = (query: MediaQueryList | undefined) => {
if (!query) return;
if (typeof query.removeEventListener === 'function') {
query.removeEventListener('change', update);
} else if (typeof query.removeListener === 'function') {
query.removeListener(update);
}
};
addQueryListener(standaloneQuery);
addQueryListener(fullscreenQuery);
return () => {
window.removeEventListener('resize', update);
window.removeEventListener('focus', update);
removeQueryListener(standaloneQuery);
removeQueryListener(fullscreenQuery);
};
}, []);
return value;
}
export function useDeviceInfo(): DeviceInfo {
const [deviceInfo, setDeviceInfo] = React.useState<DeviceInfo>(() => {
if (typeof window === 'undefined') {