1+ import * as tabsets from "./tabsets/tabsets.js" ;
2+
13const sectionChanged = new CustomEvent ( "quarto-sectionChanged" , {
24 detail : { } ,
35 bubbles : true ,
@@ -64,19 +66,41 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
6466 }
6567 } ;
6668
67- // fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
68- function fireSlideEnter ( e ) {
69+ // dispatch for htmlwidgets
70+ // they use slideenter event to trigger resize
71+ function fireSlideEnter ( ) {
6972 const event = window . document . createEvent ( "Event" ) ;
7073 event . initEvent ( "slideenter" , true , true ) ;
7174 window . document . dispatchEvent ( event ) ;
7275 }
76+
7377 const tabs = window . document . querySelectorAll ( 'a[data-bs-toggle="tab"]' ) ;
7478 tabs . forEach ( ( tab ) => {
7579 tab . addEventListener ( "shown.bs.tab" , fireSlideEnter ) ;
7680 } ) ;
7781
78- // fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
79- document . addEventListener ( "tabby" , fireSlideEnter , false ) ;
82+ // dispatch for shiny
83+ // they use BS shown and hidden events to trigger rendering
84+ function distpatchShinyEvents ( previous , current ) {
85+ if ( window . jQuery ) {
86+ if ( previous ) {
87+ window . jQuery ( previous ) . trigger ( "hidden" ) ;
88+ }
89+ if ( current ) {
90+ window . jQuery ( current ) . trigger ( "shown" ) ;
91+ }
92+ }
93+ }
94+
95+ // tabby.js listener: Trigger event for htmlwidget and shiny
96+ document . addEventListener (
97+ "tabby" ,
98+ function ( event ) {
99+ fireSlideEnter ( ) ;
100+ distpatchShinyEvents ( event . detail . previousTab , event . detail . tab ) ;
101+ } ,
102+ false
103+ ) ;
80104
81105 // Track scrolling and mark TOC links as active
82106 // get table of contents and sidebar (bail if we don't have at least one)
@@ -94,7 +118,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
94118 if ( link . href . indexOf ( "#" ) !== - 1 ) {
95119 const anchor = link . href . split ( "#" ) [ 1 ] ;
96120 const heading = window . document . querySelector (
97- `[data-anchor-id=${ anchor } ]`
121+ `[data-anchor-id=" ${ anchor } " ]`
98122 ) ;
99123 if ( heading ) {
100124 // Add the class
@@ -134,8 +158,10 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
134158 window . innerHeight + window . pageYOffset >=
135159 window . document . body . offsetHeight
136160 ) {
161+ // This is the no-scroll case where last section should be the active one
137162 sectionIndex = 0 ;
138163 } else {
164+ // This finds the last section visible on screen that should be made active
139165 sectionIndex = [ ...sections ] . reverse ( ) . findIndex ( ( section ) => {
140166 if ( section ) {
141167 return window . pageYOffset >= section . offsetTop - sectionMargin ;
@@ -223,17 +249,21 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
223249 }
224250
225251 async function findAndActivateCategories ( ) {
226- const currentPagePath = offsetAbsoluteUrl ( window . location . href ) ;
252+ // Categories search with listing only use path without query
253+ const currentPagePath = offsetAbsoluteUrl (
254+ window . location . origin + window . location . pathname
255+ ) ;
227256 const response = await fetch ( offsetRelativeUrl ( "listings.json" ) ) ;
228257 if ( response . status == 200 ) {
229258 return response . json ( ) . then ( function ( listingPaths ) {
230259 const listingHrefs = [ ] ;
231260 for ( const listingPath of listingPaths ) {
232261 const pathWithoutLeadingSlash = listingPath . listing . substring ( 1 ) ;
233262 for ( const item of listingPath . items ) {
263+ const encodedItem = encodeURI ( item ) ;
234264 if (
235- item === currentPagePath ||
236- item === currentPagePath + "index.html"
265+ encodedItem === currentPagePath ||
266+ encodedItem === currentPagePath + "index.html"
237267 ) {
238268 // Resolve this path against the offset to be sure
239269 // we already are using the correct path to the listing
@@ -317,6 +347,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
317347 for ( const child of el . children ) {
318348 child . style . opacity = 0 ;
319349 child . style . overflow = "hidden" ;
350+ child . style . pointerEvents = "none" ;
320351 }
321352
322353 nexttick ( ( ) => {
@@ -358,6 +389,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
358389
359390 const clone = child . cloneNode ( true ) ;
360391 clone . style . opacity = 1 ;
392+ clone . style . pointerEvents = null ;
361393 clone . style . display = null ;
362394 toggleContents . append ( clone ) ;
363395 }
@@ -432,6 +464,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
432464 for ( const child of el . children ) {
433465 child . style . opacity = 1 ;
434466 child . style . overflow = null ;
467+ child . style . pointerEvents = null ;
435468 }
436469
437470 const placeholderEl = window . document . getElementById (
@@ -732,13 +765,14 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
732765
733766 // See if there is an active child to this element
734767 let hasActiveChild = false ;
735- for ( child of el . children ) {
768+ for ( const child of el . children ) {
736769 hasActiveChild = walk ( child , depth ) || hasActiveChild ;
737770 }
738771
739772 // Process the collapse state if this is an UL
740773 if ( el . tagName === "UL" ) {
741774 if ( tocOpenDepth === - 1 && depth > 1 ) {
775+ // toc-expand: false
742776 el . classList . add ( "collapse" ) ;
743777 } else if (
744778 depth <= tocOpenDepth ||
@@ -757,10 +791,9 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
757791 } ;
758792
759793 // walk the TOC and expand / collapse any items that should be shown
760-
761794 if ( tocEl ) {
762- walk ( tocEl , 0 ) ;
763795 updateActiveLink ( ) ;
796+ walk ( tocEl , 0 ) ;
764797 }
765798
766799 // Throttle the scroll event and walk peridiocally
@@ -779,6 +812,10 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
779812 window . addEventListener (
780813 "resize" ,
781814 throttle ( ( ) => {
815+ if ( tocEl ) {
816+ updateActiveLink ( ) ;
817+ walk ( tocEl , 0 ) ;
818+ }
782819 if ( ! isReaderMode ( ) ) {
783820 hideOverlappedSidebars ( ) ;
784821 }
@@ -788,98 +825,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
788825 highlightReaderToggle ( isReaderMode ( ) ) ;
789826} ) ;
790827
791- // grouped tabsets
792- window . addEventListener ( "pageshow" , ( _event ) => {
793- function getTabSettings ( ) {
794- const data = localStorage . getItem ( "quarto-persistent-tabsets-data" ) ;
795- if ( ! data ) {
796- localStorage . setItem ( "quarto-persistent-tabsets-data" , "{}" ) ;
797- return { } ;
798- }
799- if ( data ) {
800- return JSON . parse ( data ) ;
801- }
802- }
803-
804- function setTabSettings ( data ) {
805- localStorage . setItem (
806- "quarto-persistent-tabsets-data" ,
807- JSON . stringify ( data )
808- ) ;
809- }
810-
811- function setTabState ( groupName , groupValue ) {
812- const data = getTabSettings ( ) ;
813- data [ groupName ] = groupValue ;
814- setTabSettings ( data ) ;
815- }
816-
817- function toggleTab ( tab , active ) {
818- const tabPanelId = tab . getAttribute ( "aria-controls" ) ;
819- const tabPanel = document . getElementById ( tabPanelId ) ;
820- if ( active ) {
821- tab . classList . add ( "active" ) ;
822- tabPanel . classList . add ( "active" ) ;
823- } else {
824- tab . classList . remove ( "active" ) ;
825- tabPanel . classList . remove ( "active" ) ;
826- }
827- }
828-
829- function toggleAll ( selectedGroup , selectorsToSync ) {
830- for ( const [ thisGroup , tabs ] of Object . entries ( selectorsToSync ) ) {
831- const active = selectedGroup === thisGroup ;
832- for ( const tab of tabs ) {
833- toggleTab ( tab , active ) ;
834- }
835- }
836- }
837-
838- function findSelectorsToSyncByLanguage ( ) {
839- const result = { } ;
840- const tabs = Array . from (
841- document . querySelectorAll ( `div[data-group] a[id^='tabset-']` )
842- ) ;
843- for ( const item of tabs ) {
844- const div = item . parentElement . parentElement . parentElement ;
845- const group = div . getAttribute ( "data-group" ) ;
846- if ( ! result [ group ] ) {
847- result [ group ] = { } ;
848- }
849- const selectorsToSync = result [ group ] ;
850- const value = item . innerHTML ;
851- if ( ! selectorsToSync [ value ] ) {
852- selectorsToSync [ value ] = [ ] ;
853- }
854- selectorsToSync [ value ] . push ( item ) ;
855- }
856- return result ;
857- }
858-
859- function setupSelectorSync ( ) {
860- const selectorsToSync = findSelectorsToSyncByLanguage ( ) ;
861- Object . entries ( selectorsToSync ) . forEach ( ( [ group , tabSetsByValue ] ) => {
862- Object . entries ( tabSetsByValue ) . forEach ( ( [ value , items ] ) => {
863- items . forEach ( ( item ) => {
864- item . addEventListener ( "click" , ( _event ) => {
865- setTabState ( group , value ) ;
866- toggleAll ( value , selectorsToSync [ group ] ) ;
867- } ) ;
868- } ) ;
869- } ) ;
870- } ) ;
871- return selectorsToSync ;
872- }
873-
874- const selectorsToSync = setupSelectorSync ( ) ;
875- for ( const [ group , selectedName ] of Object . entries ( getTabSettings ( ) ) ) {
876- const selectors = selectorsToSync [ group ] ;
877- // it's possible that stale state gives us empty selections, so we explicitly check here.
878- if ( selectors ) {
879- toggleAll ( selectedName , selectors ) ;
880- }
881- }
882- } ) ;
828+ tabsets . init ( ) ;
883829
884830function throttle ( func , wait ) {
885831 let waiting = false ;
0 commit comments