1
+ import * as tabsets from "./tabsets/tabsets.js" ;
2
+
1
3
const sectionChanged = new CustomEvent ( "quarto-sectionChanged" , {
2
4
detail : { } ,
3
5
bubbles : true ,
@@ -64,19 +66,41 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
64
66
}
65
67
} ;
66
68
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 ( ) {
69
72
const event = window . document . createEvent ( "Event" ) ;
70
73
event . initEvent ( "slideenter" , true , true ) ;
71
74
window . document . dispatchEvent ( event ) ;
72
75
}
76
+
73
77
const tabs = window . document . querySelectorAll ( 'a[data-bs-toggle="tab"]' ) ;
74
78
tabs . forEach ( ( tab ) => {
75
79
tab . addEventListener ( "shown.bs.tab" , fireSlideEnter ) ;
76
80
} ) ;
77
81
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
+ ) ;
80
104
81
105
// Track scrolling and mark TOC links as active
82
106
// 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) {
94
118
if ( link . href . indexOf ( "#" ) !== - 1 ) {
95
119
const anchor = link . href . split ( "#" ) [ 1 ] ;
96
120
const heading = window . document . querySelector (
97
- `[data-anchor-id=${ anchor } ]`
121
+ `[data-anchor-id=" ${ anchor } " ]`
98
122
) ;
99
123
if ( heading ) {
100
124
// Add the class
@@ -134,8 +158,10 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
134
158
window . innerHeight + window . pageYOffset >=
135
159
window . document . body . offsetHeight
136
160
) {
161
+ // This is the no-scroll case where last section should be the active one
137
162
sectionIndex = 0 ;
138
163
} else {
164
+ // This finds the last section visible on screen that should be made active
139
165
sectionIndex = [ ...sections ] . reverse ( ) . findIndex ( ( section ) => {
140
166
if ( section ) {
141
167
return window . pageYOffset >= section . offsetTop - sectionMargin ;
@@ -223,17 +249,21 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
223
249
}
224
250
225
251
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
+ ) ;
227
256
const response = await fetch ( offsetRelativeUrl ( "listings.json" ) ) ;
228
257
if ( response . status == 200 ) {
229
258
return response . json ( ) . then ( function ( listingPaths ) {
230
259
const listingHrefs = [ ] ;
231
260
for ( const listingPath of listingPaths ) {
232
261
const pathWithoutLeadingSlash = listingPath . listing . substring ( 1 ) ;
233
262
for ( const item of listingPath . items ) {
263
+ const encodedItem = encodeURI ( item ) ;
234
264
if (
235
- item === currentPagePath ||
236
- item === currentPagePath + "index.html"
265
+ encodedItem === currentPagePath ||
266
+ encodedItem === currentPagePath + "index.html"
237
267
) {
238
268
// Resolve this path against the offset to be sure
239
269
// we already are using the correct path to the listing
@@ -317,6 +347,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
317
347
for ( const child of el . children ) {
318
348
child . style . opacity = 0 ;
319
349
child . style . overflow = "hidden" ;
350
+ child . style . pointerEvents = "none" ;
320
351
}
321
352
322
353
nexttick ( ( ) => {
@@ -358,6 +389,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
358
389
359
390
const clone = child . cloneNode ( true ) ;
360
391
clone . style . opacity = 1 ;
392
+ clone . style . pointerEvents = null ;
361
393
clone . style . display = null ;
362
394
toggleContents . append ( clone ) ;
363
395
}
@@ -432,6 +464,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
432
464
for ( const child of el . children ) {
433
465
child . style . opacity = 1 ;
434
466
child . style . overflow = null ;
467
+ child . style . pointerEvents = null ;
435
468
}
436
469
437
470
const placeholderEl = window . document . getElementById (
@@ -732,13 +765,14 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
732
765
733
766
// See if there is an active child to this element
734
767
let hasActiveChild = false ;
735
- for ( child of el . children ) {
768
+ for ( const child of el . children ) {
736
769
hasActiveChild = walk ( child , depth ) || hasActiveChild ;
737
770
}
738
771
739
772
// Process the collapse state if this is an UL
740
773
if ( el . tagName === "UL" ) {
741
774
if ( tocOpenDepth === - 1 && depth > 1 ) {
775
+ // toc-expand: false
742
776
el . classList . add ( "collapse" ) ;
743
777
} else if (
744
778
depth <= tocOpenDepth ||
@@ -757,10 +791,9 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
757
791
} ;
758
792
759
793
// walk the TOC and expand / collapse any items that should be shown
760
-
761
794
if ( tocEl ) {
762
- walk ( tocEl , 0 ) ;
763
795
updateActiveLink ( ) ;
796
+ walk ( tocEl , 0 ) ;
764
797
}
765
798
766
799
// Throttle the scroll event and walk peridiocally
@@ -779,6 +812,10 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
779
812
window . addEventListener (
780
813
"resize" ,
781
814
throttle ( ( ) => {
815
+ if ( tocEl ) {
816
+ updateActiveLink ( ) ;
817
+ walk ( tocEl , 0 ) ;
818
+ }
782
819
if ( ! isReaderMode ( ) ) {
783
820
hideOverlappedSidebars ( ) ;
784
821
}
@@ -788,98 +825,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
788
825
highlightReaderToggle ( isReaderMode ( ) ) ;
789
826
} ) ;
790
827
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 ( ) ;
883
829
884
830
function throttle ( func , wait ) {
885
831
let waiting = false ;
0 commit comments