@@ -937,6 +937,108 @@ FrameTrail.defineType(
937937 }
938938 } ) ;
939939
940+ // Horizontal swipe to navigate between collection elements.
941+ // Uses touchstart/touchmove because <video controls> swallows
942+ // pointer events at the browser level (even capture phase on
943+ // ancestors never fires). Touch events DO still propagate.
944+ // Vertical scroll is preserved by only acting when the gesture
945+ // is predominantly horizontal.
946+ ( function ( ) {
947+ var startX = 0 , startY = 0 , swiping = false , decided = false ;
948+ var THRESHOLD = 40 ; // min px to trigger a swipe
949+ var ANGLE_LIMIT = 30 ; // max degrees from horizontal
950+
951+ detailsContainerElement . addEventListener ( 'touchstart' , function ( e ) {
952+ if ( e . touches . length !== 1 ) return ;
953+ startX = e . touches [ 0 ] . clientX ;
954+ startY = e . touches [ 0 ] . clientY ;
955+ swiping = true ;
956+ decided = false ;
957+ } , true ) ;
958+
959+ detailsContainerElement . addEventListener ( 'touchmove' , function ( e ) {
960+ if ( ! swiping || decided ) return ;
961+ var touch = e . touches [ 0 ] ;
962+ var dx = touch . clientX - startX ;
963+ var dy = touch . clientY - startY ;
964+ var dist = Math . sqrt ( dx * dx + dy * dy ) ;
965+ if ( dist < THRESHOLD ) return ;
966+
967+ decided = true ;
968+ var angle = Math . abs ( Math . atan2 ( dy , dx ) * 180 / Math . PI ) ;
969+ if ( angle > ANGLE_LIMIT && angle < ( 180 - ANGLE_LIMIT ) ) {
970+ swiping = false ;
971+ return ;
972+ }
973+
974+ swiping = false ;
975+ e . preventDefault ( ) ;
976+
977+ var activeElement = self . contentViewContainer . querySelector ( '.collectionElement.open' ) ;
978+ if ( ! activeElement ) return ;
979+
980+ if ( dx < 0 ) {
981+ var next = activeElement . nextElementSibling ;
982+ while ( next && ! next . classList . contains ( 'collectionElement' ) ) { next = next . nextElementSibling ; }
983+ if ( next ) next . click ( ) ;
984+ } else {
985+ var prev = activeElement . previousElementSibling ;
986+ while ( prev && ! prev . classList . contains ( 'collectionElement' ) ) { prev = prev . previousElementSibling ; }
987+ if ( prev ) prev . click ( ) ;
988+ }
989+ } , true ) ;
990+
991+ detailsContainerElement . addEventListener ( 'touchend' , function ( ) { swiping = false ; decided = false ; } , true ) ;
992+ detailsContainerElement . addEventListener ( 'touchcancel' , function ( ) { swiping = false ; decided = false ; } , true ) ;
993+
994+ // Also support mouse-based swipe (desktop, non-touch)
995+ detailsContainerElement . addEventListener ( 'pointerdown' , function ( e ) {
996+ if ( e . pointerType === 'touch' ) return ; // handled by touch events above
997+ if ( e . button !== 0 ) return ;
998+ startX = e . clientX ;
999+ startY = e . clientY ;
1000+ swiping = true ;
1001+ decided = false ;
1002+ } , true ) ;
1003+
1004+ detailsContainerElement . addEventListener ( 'pointermove' , function ( e ) {
1005+ if ( e . pointerType === 'touch' ) return ;
1006+ if ( ! swiping || decided ) return ;
1007+ var dx = e . clientX - startX ;
1008+ var dy = e . clientY - startY ;
1009+ var dist = Math . sqrt ( dx * dx + dy * dy ) ;
1010+ if ( dist < THRESHOLD ) return ;
1011+
1012+ decided = true ;
1013+ var angle = Math . abs ( Math . atan2 ( dy , dx ) * 180 / Math . PI ) ;
1014+ if ( angle > ANGLE_LIMIT && angle < ( 180 - ANGLE_LIMIT ) ) {
1015+ swiping = false ;
1016+ return ;
1017+ }
1018+
1019+ swiping = false ;
1020+ var activeElement = self . contentViewContainer . querySelector ( '.collectionElement.open' ) ;
1021+ if ( ! activeElement ) return ;
1022+
1023+ if ( dx < 0 ) {
1024+ var next = activeElement . nextElementSibling ;
1025+ while ( next && ! next . classList . contains ( 'collectionElement' ) ) { next = next . nextElementSibling ; }
1026+ if ( next ) next . click ( ) ;
1027+ } else {
1028+ var prev = activeElement . previousElementSibling ;
1029+ while ( prev && ! prev . classList . contains ( 'collectionElement' ) ) { prev = prev . previousElementSibling ; }
1030+ if ( prev ) prev . click ( ) ;
1031+ }
1032+ } , true ) ;
1033+
1034+ detailsContainerElement . addEventListener ( 'pointerup' , function ( e ) {
1035+ if ( e . pointerType !== 'touch' ) { swiping = false ; decided = false ; }
1036+ } , true ) ;
1037+ detailsContainerElement . addEventListener ( 'pointercancel' , function ( e ) {
1038+ if ( e . pointerType !== 'touch' ) { swiping = false ; decided = false ; }
1039+ } , true ) ;
1040+ } ) ( ) ;
1041+
9401042 return detailsContainerElement ;
9411043
9421044 } ,
0 commit comments