Files
suwayomi-material-you-webui/src/modules/core/utils/MediaQuery.tsx

116 lines
4.0 KiB
TypeScript
Raw Normal View History

2024-05-23 02:28:37 +02:00
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import useMediaQuery from '@mui/material/useMediaQuery';
import { Breakpoint } from '@mui/material/styles';
2024-09-06 23:43:38 +02:00
import { useCallback, useState } from 'react';
import { getCurrentTheme } from '@/modules/theme/services/ThemeCreator.ts';
import { ThemeMode } from '@/modules/theme/contexts/AppThemeContext.tsx';
2024-10-05 23:22:42 +02:00
import { AppStorage } from '@/lib/storage/AppStorage.ts';
2024-10-05 16:09:34 +02:00
import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
2024-05-23 02:28:37 +02:00
export class MediaQuery {
2024-11-06 13:00:36 +01:00
static readonly MOBILE_WIDTH: Breakpoint | number = 'sm';
static readonly TABLET_WIDTH: Breakpoint | number = 1025;
static isTouchDevice(): boolean {
return window.matchMedia('not (pointer: fine)').matches;
}
2024-05-23 02:28:37 +02:00
static useIsTouchDevice(): boolean {
return useMediaQuery('not (pointer: fine)');
}
2024-05-23 02:37:40 +02:00
2024-11-06 13:00:36 +01:00
static useIsBelowWidth(breakpoint: Breakpoint | number): boolean {
return useMediaQuery(getCurrentTheme().breakpoints.down(breakpoint));
}
2024-05-23 02:37:40 +02:00
static useIsMobileWidth(): boolean {
2024-11-06 13:00:36 +01:00
return this.useIsBelowWidth(this.MOBILE_WIDTH);
}
static useIsTabletWidth(): boolean {
return this.useIsBelowWidth(this.TABLET_WIDTH);
2024-05-23 02:37:40 +02:00
}
2024-09-04 23:46:04 +02:00
2024-09-28 03:24:16 +02:00
private static getScrollbarSize(type: 'height' | 'width'): number {
const outer = document.createElement('div');
outer.style.position = 'absolute';
outer.style.top = '-9999px';
2024-09-28 03:24:16 +02:00
outer.style.visibility = 'hidden';
outer.style.overflow = 'scroll';
document.body.appendChild(outer);
const inner = document.createElement('div');
inner.style.width = '100%';
inner.style.height = '100%';
outer.appendChild(inner);
const width = outer.offsetWidth - inner.offsetWidth;
const height = outer.offsetHeight - inner.offsetHeight;
document.body.removeChild(outer);
2024-09-28 03:24:16 +02:00
return type === 'height' ? height : width;
}
static useGetScrollbarSize(
type: 'height' | 'width',
element: HTMLElement | null = document.documentElement,
): number {
2024-09-06 23:43:38 +02:00
const [scrollbarSize, setScrollbarSize] = useState(0);
useResizeObserver(
2024-09-28 03:24:16 +02:00
element,
2024-09-06 23:50:38 +02:00
useCallback(() => {
2024-09-28 03:24:16 +02:00
const hasYScrollbar = !!(element!.scrollHeight - element!.clientHeight);
const hasXScrollbar = !!(element!.scrollWidth - element!.clientWidth);
const hasScrollbar = (type === 'height' && hasYScrollbar) || (type === 'width' && hasXScrollbar);
if (hasScrollbar) {
setScrollbarSize(this.getScrollbarSize(type));
return;
}
2024-09-06 23:50:38 +02:00
2024-09-28 03:24:16 +02:00
setScrollbarSize(0);
}, [element]),
2024-09-06 23:43:38 +02:00
);
return scrollbarSize;
}
2024-09-04 23:46:04 +02:00
static getSystemThemeMode(): Exclude<ThemeMode, 'system'> {
const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
return prefersDarkMode ? ThemeMode.DARK : ThemeMode.LIGHT;
}
2024-09-05 00:08:23 +02:00
static getThemeMode(): Exclude<ThemeMode, 'system'> {
const themeMode = AppStorage.local.getItem('mui-mode') as ThemeMode;
2024-09-05 00:08:23 +02:00
const isSystemMode = themeMode === ThemeMode.SYSTEM;
if (isSystemMode) {
return this.getSystemThemeMode();
}
return themeMode;
}
2024-09-04 23:46:04 +02:00
static listenToSystemThemeChange(onChange: (themeMode: Exclude<ThemeMode, 'system'>) => void): () => void {
const handleSystemThemeModeChange = (e: MediaQueryListEvent) => {
onChange(e.matches ? ThemeMode.DARK : ThemeMode.LIGHT);
};
const matchSystemThemeMode = window.matchMedia('(prefers-color-scheme: dark)');
matchSystemThemeMode.addEventListener('change', handleSystemThemeModeChange);
return () => matchSystemThemeMode.removeEventListener('change', handleSystemThemeModeChange);
}
2024-05-23 02:28:37 +02:00
}