1- import { watch , onMounted , ref , Ref , ComputedRef , onBeforeUnmount , computed , nextTick } from 'vue' ;
1+ import { watch , onMounted , ref , Ref , ComputedRef , onBeforeUnmount , computed , customRef } from 'vue' ;
22import { isSSR } from './utils' ;
33
44type UseListenersOptions = {
@@ -9,61 +9,52 @@ type UseListenersOptions = {
99 minWidth : number ;
1010} ;
1111
12+ const ONCE = { once : true } ;
13+
1214export function useListeners ( { isHTML, root, rootTop, _setActive, minWidth } : UseListenersOptions ) {
13- const isClick = ref ( false ) ;
15+ const isClick = customRef < boolean > ( ( track , trigger ) => {
16+ let value = false ;
17+ return {
18+ get ( ) {
19+ track ( ) ;
20+ return value ;
21+ } ,
22+ set ( newValue ) {
23+ value = matchMedia . value ? newValue : false ;
24+ trigger ( ) ;
25+ } ,
26+ } ;
27+ } ) ;
1428
1529 if ( isSSR ) {
1630 return isClick ;
1731 }
1832
1933 const media = `(min-width: ${ minWidth } px)` ;
20-
2134 const matchMedia = ref ( window . matchMedia ( media ) . matches ) ;
2235 const isIdle = ref ( false ) ;
23-
24- const clickY = computed ( ( ) => {
25- if ( isClick . value ) {
26- return getNextY ( ) ;
27- }
28- } ) ;
36+ const clickY = computed ( ( ) => ( isClick . value ? getNextY ( ) : 0 ) ) ;
2937
3038 let prevY : number ;
3139
3240 function getNextY ( ) {
3341 return isHTML . value ? window . scrollY : root . value ! . scrollTop ;
3442 }
3543
36- function onResize ( ) {
37- rootTop . value = isHTML . value ? 0 : root . value ! . getBoundingClientRect ( ) . top ;
38- matchMedia . value = window . matchMedia ( media ) . matches ;
39- }
40-
41- function onScroll ( ) {
42- // Do not update results if scrolling from click
43- if ( ! isClick . value ) {
44- const nextY = getNextY ( ) ;
45- if ( ! prevY ) {
46- prevY = nextY ;
47- }
48- _setActive ( prevY ) ;
49- prevY = nextY ;
50- }
51- }
52-
5344 function setReady ( ) {
54- let prevY : number ;
45+ let rafPrevY : number ;
5546 let rafId : DOMHighResTimeStamp ;
5647 let frameCount = 0 ;
5748
5849 function scrollEnd ( ) {
59- const nextY = getNextY ( ) ;
60- if ( typeof prevY === 'undefined' || prevY !== nextY ) {
50+ const rafNextY = getNextY ( ) ;
51+ if ( typeof rafPrevY === 'undefined' || rafPrevY !== rafNextY ) {
6152 frameCount = 0 ;
62- prevY = nextY ;
53+ rafPrevY = rafNextY ;
6354 // console.log('Scrolling...');
6455 return requestAnimationFrame ( scrollEnd ) ;
6556 }
66- // When equal, wait at least 20 frames to be sure is idle
57+ // When equal, wait for 20 frames to be sure is idle
6758 frameCount ++ ;
6859 if ( frameCount === 20 ) {
6960 isIdle . value = true ;
@@ -78,6 +69,23 @@ export function useListeners({ isHTML, root, rootTop, _setActive, minWidth }: Us
7869 rafId = requestAnimationFrame ( scrollEnd ) ;
7970 }
8071
72+ function onResize ( ) {
73+ rootTop . value = isHTML . value ? 0 : root . value ! . getBoundingClientRect ( ) . top ;
74+ matchMedia . value = window . matchMedia ( media ) . matches ;
75+ }
76+
77+ function onScroll ( ) {
78+ // Do not "update" results if scrolling from click
79+ if ( ! isClick . value ) {
80+ const nextY = getNextY ( ) ;
81+ if ( ! prevY ) {
82+ prevY = nextY ;
83+ }
84+ _setActive ( prevY ) ;
85+ prevY = nextY ;
86+ }
87+ }
88+
8189 // Restore main listener "updating" functionalities if scrolling again while scrolling from click...
8290 function reScroll ( ) {
8391 isClick . value = false ;
@@ -90,19 +98,23 @@ export function useListeners({ isHTML, root, rootTop, _setActive, minWidth }: Us
9098 }
9199
92100 function onPointerDown ( event : PointerEvent ) {
93- reScroll ( ) ;
94- const { tagName } = event . target as HTMLElement ;
95- const isLink = tagName === 'A' || tagName === 'BUTTON' ;
96- if ( ! isLink ) {
101+ const isLink = ( event . target as HTMLElement ) . tagName === 'A' ;
102+ const hasLink = ( event . target as HTMLElement ) . closest ( 'a' ) ;
103+ if ( ! isLink && ! hasLink ) {
104+ reScroll ( ) ;
97105 // ...and force set if canceling scroll
98106 _setActive ( clickY . value ! , { isCancel : true } ) ;
99107 }
100108 }
101109
102110 onMounted ( ( ) => {
103111 window . addEventListener ( 'resize' , onResize , { passive : true } ) ;
104- // Wait for any eventual scroll to hash triggered by browser to end
105- setReady ( ) ;
112+ if ( matchMedia . value ) {
113+ // Wait for any eventual scroll to hash triggered by browser to end
114+ setReady ( ) ;
115+ } else {
116+ isIdle . value = true ;
117+ }
106118 } ) ;
107119
108120 onBeforeUnmount ( ( ) => {
@@ -111,21 +123,18 @@ export function useListeners({ isHTML, root, rootTop, _setActive, minWidth }: Us
111123
112124 watch (
113125 [ isIdle , matchMedia , root ] ,
114- ( [ isIdle , matchMedia , root ] , [ ] , onCleanup ) => {
126+ ( [ _isIdle , _matchMedia , root ] , [ ] , onCleanup ) => {
115127 const rootEl = isHTML . value ? document : root ;
116- if ( isIdle && rootEl ) {
117- if ( matchMedia ) {
118- console . log ( 'Adding main listener...' ) ;
119- rootEl . addEventListener ( 'scroll' , onScroll , {
120- passive : true ,
121- } ) ;
122- }
128+
129+ if ( _isIdle && rootEl && _matchMedia ) {
130+ console . log ( 'Adding main listener...' ) ;
131+ rootEl . addEventListener ( 'scroll' , onScroll , {
132+ passive : true ,
133+ } ) ;
123134
124135 onCleanup ( ( ) => {
125- if ( matchMedia ) {
126- console . log ( 'Removing main listener...' ) ;
127- rootEl . removeEventListener ( 'scroll' , onScroll ) ;
128- }
136+ console . log ( 'Removing main listener...' ) ;
137+ rootEl . removeEventListener ( 'scroll' , onScroll ) ;
129138 } ) ;
130139 }
131140 } ,
@@ -134,23 +143,19 @@ export function useListeners({ isHTML, root, rootTop, _setActive, minWidth }: Us
134143
135144 watch (
136145 isClick ,
137- ( isClick , _ , onCleanup ) => {
146+ ( _isClick , _ , onCleanup ) => {
138147 const rootEl = isHTML . value ? document : root . value ! ;
139148
140- if ( isClick ) {
149+ if ( _isClick ) {
141150 console . log ( 'Adding additional listeners...' ) ;
142- rootEl . addEventListener ( 'scroll' , setReady , { once : true } ) ;
143- rootEl . addEventListener ( 'wheel' , reScroll , { once : true } ) ;
144- rootEl . addEventListener ( 'keydown' , onSpaceBar as EventListener , {
145- once : true ,
146- } ) ;
147- rootEl . addEventListener ( 'pointerdown' , onPointerDown as EventListener , {
148- once : true ,
149- } ) ;
151+ rootEl . addEventListener ( 'scroll' , setReady , ONCE ) ;
152+ rootEl . addEventListener ( 'wheel' , reScroll , ONCE ) ;
153+ rootEl . addEventListener ( 'keydown' , onSpaceBar as EventListener , ONCE ) ;
154+ rootEl . addEventListener ( 'pointerdown' , onPointerDown as EventListener , ONCE ) ;
150155 }
151156
152157 onCleanup ( ( ) => {
153- if ( isClick ) {
158+ if ( _isClick ) {
154159 console . log ( 'Removing additional listeners...' ) ;
155160 rootEl . removeEventListener ( 'scroll' , setReady ) ;
156161 rootEl . removeEventListener ( 'wheel' , reScroll ) ;
0 commit comments