@@ -2,12 +2,52 @@ import { ref } from 'vue'
22import type { Ref } from 'vue'
33
44const MOBILE_BREAKPOINT = '(max-width: 768px)'
5+ const MOBILE_BREAKPOINT_WIDTH = 768
6+ const LAYOUT_STABILIZE_DELAY_MS = 120
57
68const mediaQuery = window . matchMedia ( MOBILE_BREAKPOINT )
7- const isMobile = ref ( mediaQuery . matches )
89
9- mediaQuery . addEventListener ( 'change' , ( e ) => {
10- isMobile . value = e . matches
10+ const readViewportWidth = ( ) : number | null => {
11+ const candidates = [
12+ window . visualViewport ?. width ,
13+ window . innerWidth ,
14+ document . documentElement ?. clientWidth ,
15+ ] . filter ( ( value ) : value is number => typeof value === 'number' && Number . isFinite ( value ) )
16+
17+ const width = Math . max ( 0 , ...candidates )
18+ return width > 0 ? width : null
19+ }
20+
21+ const readStableMobileState = ( ) : boolean | null => {
22+ if ( document . visibilityState === 'hidden' ) return null
23+ const width = readViewportWidth ( )
24+ if ( width == null ) return null
25+ return width <= MOBILE_BREAKPOINT_WIDTH
26+ }
27+
28+ const isMobile = ref ( readStableMobileState ( ) ?? mediaQuery . matches )
29+ let pendingUpdate : ReturnType < typeof setTimeout > | null = null
30+
31+ const commitStableMobileState = ( ) => {
32+ pendingUpdate = null
33+ const next = readStableMobileState ( )
34+ if ( next == null ) return
35+ isMobile . value = next
36+ }
37+
38+ const scheduleStableMobileUpdate = ( ) => {
39+ if ( pendingUpdate ) clearTimeout ( pendingUpdate )
40+ pendingUpdate = setTimeout ( commitStableMobileState , LAYOUT_STABILIZE_DELAY_MS )
41+ }
42+
43+ mediaQuery . addEventListener ( 'change' , ( ) => {
44+ scheduleStableMobileUpdate ( )
45+ } )
46+
47+ window . addEventListener ( 'resize' , scheduleStableMobileUpdate )
48+ window . visualViewport ?. addEventListener ( 'resize' , scheduleStableMobileUpdate )
49+ document . addEventListener ( 'visibilitychange' , ( ) => {
50+ if ( document . visibilityState === 'visible' ) scheduleStableMobileUpdate ( )
1151} )
1252
1353export function useIsMobile ( ) : { isMobile : Ref < boolean > } {
0 commit comments