@@ -345,7 +345,8 @@ interface Domain {
345345 `<span class="muted">${ over ? `${ out - LIMIT } over the ${ LIMIT } -day limit`
346346 : `${ LIMIT - out } days of headroom` } </span>` +
347347 ( selected
348- ? `<hr><span class="selection-help">ESC to dismiss selection; arrow keys or H/L adjust by day; hold Shift to adjust by week.</span>`
348+ ? `<hr><span class="selection-help keyboard-help">ESC to dismiss selection; arrow keys or H/L adjust by day; hold Shift to adjust by week.</span>` +
349+ `<span class="selection-help touch-help">Swipe left or right to adjust the window; tap elsewhere to dismiss.</span>`
349350 : "" ) ;
350351 }
351352
@@ -910,6 +911,67 @@ interface Domain {
910911 } ) ;
911912 }
912913
914+ function initChartSelectionTouchSliding ( ) : void {
915+ for ( const host of [ el ( "plot1" ) , el ( "trip-timeline" ) ] ) {
916+ let pointerId : number | null = null ;
917+ let startX = 0 ;
918+ let startY = 0 ;
919+ let startDay = 0 ;
920+ let startWindowEnd = 0 ;
921+ let dragging = false ;
922+ let suppressClick = false ;
923+
924+ host . addEventListener ( "pointerdown" , ( ev : PointerEvent ) => {
925+ if ( ev . pointerType !== "touch" || selectedWindowEnd === null || ! rollingHover ) return ;
926+ pointerId = ev . pointerId ;
927+ startX = ev . clientX ;
928+ startY = ev . clientY ;
929+ startDay = rollingHover . dateAt ( ev . clientX ) ;
930+ startWindowEnd = selectedWindowEnd ;
931+ dragging = false ;
932+ host . setPointerCapture ( ev . pointerId ) ;
933+ } ) ;
934+
935+ host . addEventListener ( "pointermove" , ( ev : PointerEvent ) => {
936+ if ( ev . pointerId !== pointerId || selectedWindowEnd === null || ! rollingHover ) return ;
937+ const dx = ev . clientX - startX ;
938+ const dy = ev . clientY - startY ;
939+ if ( ! dragging ) {
940+ if ( Math . hypot ( dx , dy ) < 6 ) return ;
941+ if ( Math . abs ( dy ) >= Math . abs ( dx ) ) {
942+ if ( host . hasPointerCapture ( ev . pointerId ) ) host . releasePointerCapture ( ev . pointerId ) ;
943+ pointerId = null ;
944+ return ;
945+ }
946+ dragging = true ;
947+ }
948+
949+ ev . preventDefault ( ) ;
950+ const dayDelta = rollingHover . dateAt ( ev . clientX ) - startDay ;
951+ const targetEnd = rollingHover . clampDate ( startWindowEnd + dayDelta ) ;
952+ const days = targetEnd - selectedWindowEnd ;
953+ if ( days !== 0 ) moveSelectedWindowBy ( days ) ;
954+ } ) ;
955+
956+ const finish = ( ev : PointerEvent ) : void => {
957+ if ( ev . pointerId !== pointerId ) return ;
958+ if ( host . hasPointerCapture ( ev . pointerId ) ) host . releasePointerCapture ( ev . pointerId ) ;
959+ suppressClick = dragging ;
960+ pointerId = null ;
961+ dragging = false ;
962+ if ( suppressClick ) setTimeout ( ( ) => { suppressClick = false ; } , 0 ) ;
963+ } ;
964+ host . addEventListener ( "pointerup" , finish ) ;
965+ host . addEventListener ( "pointercancel" , finish ) ;
966+ host . addEventListener ( "click" , ( ev : MouseEvent ) => {
967+ if ( ! suppressClick ) return ;
968+ suppressClick = false ;
969+ ev . preventDefault ( ) ;
970+ ev . stopImmediatePropagation ( ) ;
971+ } , true ) ;
972+ }
973+ }
974+
913975 function moveSelectedWindowBy ( days : number ) : void {
914976 if ( selectedWindowEnd === null || ! rollingHover || ! rollingTooltip ) return ;
915977 keepAlive ( ) ;
@@ -1238,6 +1300,7 @@ interface Domain {
12381300 initChartSelectionKeyboard ( ) ;
12391301 initChartScrollVisibility ( ) ;
12401302 initChartSelectionTouchDismissal ( ) ;
1303+ initChartSelectionTouchSliding ( ) ;
12411304 loadFromFile ( ) ;
12421305 if ( ! restoreSharedData ( ) ) restoreData ( ) ;
12431306 restoreSelectedWindow ( ) ;
0 commit comments