forked from boos/BoosAPI
feat: add mobile layout detection
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
|
describe('App mobile layout', () => {
|
||||||
|
it('sets data-layout attribute when switching layouts', () => {
|
||||||
|
const source = readFileSync(resolve(process.cwd(), 'src/web/App.tsx'), 'utf8');
|
||||||
|
|
||||||
|
expect(source).toContain('useIsMobile(768)');
|
||||||
|
expect(source).toContain("document.documentElement.setAttribute('data-layout'");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -16,6 +16,7 @@ import { I18nProvider, useI18n } from './i18n.js';
|
|||||||
import { resolveLoginErrorMessage } from './loginError.js';
|
import { resolveLoginErrorMessage } from './loginError.js';
|
||||||
import { SITE_DOCS_URL } from './docsLink.js';
|
import { SITE_DOCS_URL } from './docsLink.js';
|
||||||
import { useAnimatedVisibility } from './components/useAnimatedVisibility.js';
|
import { useAnimatedVisibility } from './components/useAnimatedVisibility.js';
|
||||||
|
import { useIsMobile } from './components/useIsMobile.js';
|
||||||
const Dashboard = lazy(() => import('./pages/Dashboard.js'));
|
const Dashboard = lazy(() => import('./pages/Dashboard.js'));
|
||||||
const Sites = lazy(() => import('./pages/Sites.js'));
|
const Sites = lazy(() => import('./pages/Sites.js'));
|
||||||
const Accounts = lazy(() => import('./pages/Accounts.js'));
|
const Accounts = lazy(() => import('./pages/Accounts.js'));
|
||||||
@@ -383,6 +384,7 @@ function AppShell() {
|
|||||||
const notifBtnRef = useRef<HTMLButtonElement>(null);
|
const notifBtnRef = useRef<HTMLButtonElement>(null);
|
||||||
const latestTaskEventIdRef = useRef(0);
|
const latestTaskEventIdRef = useRef(0);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const isMobile = useIsMobile(768);
|
||||||
const resolvedTheme: 'light' | 'dark' = themeMode === 'system'
|
const resolvedTheme: 'light' | 'dark' = themeMode === 'system'
|
||||||
? (systemPrefersDark ? 'dark' : 'light')
|
? (systemPrefersDark ? 'dark' : 'light')
|
||||||
: themeMode;
|
: themeMode;
|
||||||
@@ -415,6 +417,10 @@ function AppShell() {
|
|||||||
}
|
}
|
||||||
}, [resolvedTheme, themeMode]);
|
}, [resolvedTheme, themeMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.setAttribute('data-layout', isMobile ? 'mobile' : 'desktop');
|
||||||
|
}, [isMobile]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handler = (e: KeyboardEvent) => {
|
const handler = (e: KeyboardEvent) => {
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function useIsMobile(breakpoint = 768) {
|
||||||
|
const [isMobile, setIsMobile] = useState(() => (
|
||||||
|
typeof window !== 'undefined' ? window.innerWidth < breakpoint : false
|
||||||
|
));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const update = () => setIsMobile(window.innerWidth < breakpoint);
|
||||||
|
update();
|
||||||
|
window.addEventListener('resize', update);
|
||||||
|
return () => window.removeEventListener('resize', update);
|
||||||
|
}, [breakpoint]);
|
||||||
|
|
||||||
|
return isMobile;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user