@@ -796,6 +796,65 @@ const Hooks = {
796
796
updateStrength ( ) ;
797
797
} ,
798
798
} ,
799
+ CandidatesScroll : {
800
+ mounted ( ) {
801
+ this . handleEvent ( "scroll_to_candidate" , ( { index } ) => {
802
+ const candidateElement = document . querySelector ( `#candidate-${ index } ` ) ;
803
+ if ( candidateElement ) {
804
+ candidateElement . scrollIntoView ( {
805
+ behavior : "instant" ,
806
+ block : "start" ,
807
+ inline : "nearest"
808
+ } ) ;
809
+ }
810
+ } ) ;
811
+
812
+ // Observe candidate elements to update current index based on scroll position
813
+ this . setupScrollObserver ( ) ;
814
+ } ,
815
+
816
+ setupScrollObserver ( ) {
817
+ if ( this . observer ) {
818
+ this . observer . disconnect ( ) ;
819
+ }
820
+
821
+ this . observer = new IntersectionObserver (
822
+ ( entries ) => {
823
+ entries . forEach ( ( entry ) => {
824
+ if ( entry . isIntersecting && entry . intersectionRatio > 0.5 ) {
825
+ const candidateId = entry . target . id ;
826
+ const index = candidateId . split ( '-' ) [ 1 ] ;
827
+ if ( index !== undefined ) {
828
+ // Update the sidebar selection without triggering a scroll
829
+ this . pushEvent ( "update_current_index_silent" , { index : parseInt ( index ) } ) ;
830
+ }
831
+ }
832
+ } ) ;
833
+ } ,
834
+ {
835
+ root : null ,
836
+ rootMargin : "-20% 0px -20% 0px" ,
837
+ threshold : [ 0.5 ]
838
+ }
839
+ ) ;
840
+
841
+ // Observe all candidate elements
842
+ document . querySelectorAll ( '[id^="candidate-"]' ) . forEach ( ( el ) => {
843
+ this . observer . observe ( el ) ;
844
+ } ) ;
845
+ } ,
846
+
847
+ updated ( ) {
848
+ // Re-setup observer when candidates are updated
849
+ this . setupScrollObserver ( ) ;
850
+ } ,
851
+
852
+ destroyed ( ) {
853
+ if ( this . observer ) {
854
+ this . observer . disconnect ( ) ;
855
+ }
856
+ } ,
857
+ } ,
799
858
} satisfies Record < string , Partial < ViewHook > & Record < string , unknown > > ;
800
859
801
860
// Accessible focus handling
0 commit comments