@@ -136,6 +136,24 @@ export function bindTextLayerSelection(host, getSpans) {
136136 const document = host . ownerDocument ;
137137 const view = document . defaultView ;
138138 let gesture ;
139+ let scrollFrame , pointer ;
140+ const scroller = host . closest ( '.reader' ) ;
141+ function autoScroll ( ) {
142+ scrollFrame = 0 ;
143+ if ( ! gesture || ! pointer || ! scroller ) return ;
144+ const bounds = scroller . getBoundingClientRect ( ) ;
145+ const edge = 48 ;
146+ const speed =
147+ pointer . y < bounds . top + edge
148+ ? - Math . min ( 24 , ( bounds . top + edge - pointer . y ) / 3 )
149+ : pointer . y > bounds . bottom - edge
150+ ? Math . min ( 24 , ( pointer . y - bounds . bottom + edge ) / 3 )
151+ : 0 ;
152+ if ( ! speed ) return ;
153+ scroller . scrollTop += speed ;
154+ extend ( { clientX : pointer . x , clientY : pointer . y , preventDefault ( ) { } } , false ) ;
155+ scrollFrame = view . requestAnimationFrame ( autoScroll ) ;
156+ }
139157 // A caret in PDF whitespace must follow the nearest visual run rather than
140158 // jump to a later DOM node in another column.
141159 function caret ( x , y ) {
@@ -196,16 +214,23 @@ export function bindTextLayerSelection(host, getSpans) {
196214 . getSelection ( )
197215 ?. setBaseAndExtent ( anchor . node , anchor . offset , anchor . node , anchor . offset ) ;
198216 }
199- function extend ( event ) {
217+ function extend ( event , scheduleScroll = true ) {
200218 if ( ! gesture ) return ;
219+ pointer = { x : event . clientX , y : event . clientY } ;
220+ if ( scheduleScroll && ! scrollFrame ) scrollFrame = view . requestAnimationFrame ( autoScroll ) ;
201221 const focus = caret ( event . clientX , event . clientY ) ;
202222 if ( ! focus || ! gesture . node . isConnected ) return ;
203223 event . preventDefault ( ) ;
204224 document
205225 . getSelection ( )
206226 ?. setBaseAndExtent ( gesture . node , gesture . offset , focus . node , focus . offset ) ;
207227 }
208- const finish = ( ) => ( gesture = null ) ;
228+ const finish = ( ) => {
229+ gesture = null ;
230+ pointer = null ;
231+ view . cancelAnimationFrame ( scrollFrame ) ;
232+ scrollFrame = 0 ;
233+ } ;
209234 host . addEventListener ( 'mousedown' , begin , true ) ;
210235 textRuns . set ( host , getSpans ) ;
211236 document . addEventListener ( 'mousemove' , extend ) ;
@@ -228,6 +253,7 @@ export function bindTextLayerSelection(host, getSpans) {
228253 layers . set ( host , end ) ;
229254 if ( ! listeners ) installSharedListeners ( document ) ;
230255 return ( ) => {
256+ finish ( ) ;
231257 host . removeEventListener ( 'mousedown' , begin , true ) ;
232258 document . removeEventListener ( 'mousemove' , extend ) ;
233259 document . removeEventListener ( 'mouseup' , finish ) ;
0 commit comments