11import React from 'react' ;
2- import findIndex from 'lodash-compat/array/findIndex' ;
32import AutoAffix from 'react-overlays/lib/AutoAffix' ;
43import Waypoint from 'react-waypoint' ;
54
@@ -91,19 +90,11 @@ const sections = {
9190} ;
9291/* eslint-enable indent */
9392
94- function prevSection ( href ) {
95- let keys = Object . keys ( sections ) ;
96- let idx = findIndex ( keys , k => sections [ k ] === href ) ;
97- return sections [ keys [ Math . max ( idx - 1 , 0 ) ] ] ;
98- }
99-
100- let ScrollSpy = ( { href, onSpy } ) => (
93+ let ScrollSpy = ( { href, onBefore, onAfter } ) => (
10194 < Waypoint
102- onEnter = { ( e , dir ) => dir === 'above' && onSpy ( href ) }
103- onLeave = { ( e , dir ) => {
104- if ( dir === 'above' ) { onSpy ( href ) ; }
105- if ( dir === 'below' ) { onSpy ( prevSection ( href ) ) ; }
106- } }
95+ onEnter = { ( e , dir ) => dir === 'above' && onBefore ( href ) }
96+ onLeave = { ( e , dir ) => dir === 'above' && onAfter ( href ) }
97+ threshold = { - 0.02 }
10798 />
10899) ;
109100
@@ -120,16 +111,68 @@ const ComponentsPage = React.createClass({
120111
121112 handleNavItemSelect ( key , href ) {
122113 window . location = href ;
123- this . setActiveNavItem ( ) ;
124114 } ,
125115
126116 componentDidMount ( ) {
127- this . setActiveNavItem ( ) ;
117+ this . afterSections = { } ;
118+ Object . keys ( sections ) . forEach (
119+ key => this . afterSections [ sections [ key ] ] = false
120+ ) ;
121+
122+ const { hash } = window . location ;
123+ if ( this . afterSections [ hash ] !== undefined ) {
124+ for ( const href of Object . keys ( this . afterSections ) ) {
125+ this . afterSections [ href ] = true ;
126+
127+ if ( href === hash ) {
128+ this . setActiveNavItem ( ) ;
129+ break ;
130+ }
131+ }
132+ }
128133 } ,
129134
130- setActiveNavItem ( href = window . location . hash ) {
131- this . setState ( {
132- activeNavItemHref : href
135+ setActiveNavItem ( ) {
136+ let activeNavItemHref = null ;
137+
138+ for ( const href of Object . keys ( this . afterSections ) ) {
139+ if ( ! this . afterSections [ href ] ) {
140+ this . setState ( { activeNavItemHref } ) ;
141+ return ;
142+ }
143+
144+ activeNavItemHref = href ;
145+ }
146+ } ,
147+
148+ renderScrollSpy ( href ) {
149+ return (
150+ < ScrollSpy
151+ href = { href }
152+ onBefore = { this . onBefore }
153+ onAfter = { this . onAfter }
154+ />
155+ ) ;
156+ } ,
157+
158+ onBefore ( href ) {
159+ this . afterSections [ href ] = false ;
160+ this . updateActiveHref ( ) ;
161+ } ,
162+
163+ onAfter ( href ) {
164+ this . afterSections [ href ] = true ;
165+ this . updateActiveHref ( ) ;
166+ } ,
167+
168+ updateActiveHref ( ) {
169+ if ( this . updateActiveHrefHandle != null ) {
170+ return ;
171+ }
172+
173+ this . updateActiveHrefHandle = setTimeout ( ( ) => {
174+ this . updateActiveHrefHandle = null ;
175+ this . setActiveNavItem ( ) ;
133176 } ) ;
134177 } ,
135178
@@ -145,16 +188,16 @@ const ComponentsPage = React.createClass({
145188 < div ref = "main" className = "container bs-docs-container" >
146189 < div className = "row" >
147190 < div className = "col-md-9" role = "main" >
148- < ScrollSpy href = { sections . buttons } onSpy = { this . setActiveNavItem } />
191+ { this . renderScrollSpy ( sections . buttons ) }
149192 < ButtonSection />
150- < ScrollSpy href = { sections . btnGroups } onSpy = { this . setActiveNavItem } />
193+ { this . renderScrollSpy ( sections . btnGroups ) }
151194 < ButtonGroupSection />
152- < ScrollSpy href = { sections . dropdowns } onSpy = { this . setActiveNavItem } />
195+ { this . renderScrollSpy ( sections . dropdowns ) }
153196 < DropdownSection />
154- < ScrollSpy href = { sections . menuitems } onSpy = { this . setActiveNavItem } />
197+ { this . renderScrollSpy ( sections . menuitems ) }
155198 < MenuItemSection />
156199
157- < ScrollSpy href = { sections . overlays } onSpy = { this . setActiveNavItem } />
200+ { this . renderScrollSpy ( sections . overlays ) }
158201 < div className = "bs-docs-section" >
159202 < h1 className = "page-header" >
160203 < Anchor id = "overlays" > Overlays</ Anchor >
@@ -163,16 +206,16 @@ const ComponentsPage = React.createClass({
163206 < p className = "lead" > React-Bootstrap offers a number of accessible overlay components built using < a href = "http://react-bootstrap.github.io/react-overlays/examples/" > react-overlays</ a > .</ p >
164207 </ div >
165208
166- < ScrollSpy href = { sections . modals } onSpy = { this . setActiveNavItem } />
209+ { this . renderScrollSpy ( sections . modals ) }
167210 < ModalSection />
168- < ScrollSpy href = { sections . tooltips } onSpy = { this . setActiveNavItem } />
211+ { this . renderScrollSpy ( sections . tooltips ) }
169212 < TooltipSection />
170- < ScrollSpy href = { sections . popovers } onSpy = { this . setActiveNavItem } />
213+ { this . renderScrollSpy ( sections . popovers ) }
171214 < PopoverSection />
172- < ScrollSpy href = { sections . customOverlays } onSpy = { this . setActiveNavItem } />
215+ { this . renderScrollSpy ( sections . customOverlays ) }
173216 < OverlaySection />
174217
175- < ScrollSpy href = { sections . navigation } onSpy = { this . setActiveNavItem } />
218+ { this . renderScrollSpy ( sections . navigation ) }
176219 < div className = "bs-docs-section" >
177220 < h1 className = "page-header" >
178221 < Anchor id = "navigation" > Navigation</ Anchor >
@@ -181,20 +224,20 @@ const ComponentsPage = React.createClass({
181224 < p className = "lead" > React-Bootstrap offers a variety of responsive, accessible components for setting up navigation both across your website and within your pages.</ p >
182225 </ div >
183226
184- < ScrollSpy href = { sections . navs } onSpy = { this . setActiveNavItem } />
227+ { this . renderScrollSpy ( sections . navs ) }
185228 < NavSection />
186- < ScrollSpy href = { sections . navbars } onSpy = { this . setActiveNavItem } />
229+ { this . renderScrollSpy ( sections . navbars ) }
187230 < NavbarSection />
188- < ScrollSpy href = { sections . crumbs } onSpy = { this . setActiveNavItem } />
231+ { this . renderScrollSpy ( sections . crumbs ) }
189232 < BreadcrumbSection />
190- < ScrollSpy href = { sections . tabs } onSpy = { this . setActiveNavItem } />
233+ { this . renderScrollSpy ( sections . tabs ) }
191234 < TabsSection />
192- < ScrollSpy href = { sections . pagination } onSpy = { this . setActiveNavItem } />
235+ { this . renderScrollSpy ( sections . pagination ) }
193236 < PaginationSection />
194- < ScrollSpy href = { sections . pager } onSpy = { this . setActiveNavItem } />
237+ { this . renderScrollSpy ( sections . pager ) }
195238 < PagerSection />
196239
197- < ScrollSpy href = { sections . layout } onSpy = { this . setActiveNavItem } />
240+ { this . renderScrollSpy ( sections . layout ) }
198241 < div className = "bs-docs-section" >
199242 < h1 className = "page-header" >
200243 < Anchor id = "page-layout" > Page layout</ Anchor >
@@ -203,25 +246,25 @@ const ComponentsPage = React.createClass({
203246 < p className = "lead" > The components in this section offer different ways to structure and present data on your page.</ p >
204247 </ div >
205248
206- < ScrollSpy href = { sections . grid } onSpy = { this . setActiveNavItem } />
249+ { this . renderScrollSpy ( sections . grid ) }
207250 < GridSection />
208- < ScrollSpy href = { sections . jumbotron } onSpy = { this . setActiveNavItem } />
251+ { this . renderScrollSpy ( sections . jumbotron ) }
209252 < JumbotronSection />
210- < ScrollSpy href = { sections . pageHeader } onSpy = { this . setActiveNavItem } />
253+ { this . renderScrollSpy ( sections . pageHeader ) }
211254 < PageHeaderSection />
212- < ScrollSpy href = { sections . listGroup } onSpy = { this . setActiveNavItem } />
255+ { this . renderScrollSpy ( sections . listGroup ) }
213256 < ListGroupSection />
214- < ScrollSpy href = { sections . tables } onSpy = { this . setActiveNavItem } />
257+ { this . renderScrollSpy ( sections . tables ) }
215258 < TableSection />
216- < ScrollSpy href = { sections . panels } onSpy = { this . setActiveNavItem } />
259+ { this . renderScrollSpy ( sections . panels ) }
217260 < PanelSection />
218- < ScrollSpy href = { sections . wells } onSpy = { this . setActiveNavItem } />
261+ { this . renderScrollSpy ( sections . wells ) }
219262 < WellSection />
220263
221- < ScrollSpy href = { sections . forms } onSpy = { this . setActiveNavItem } />
264+ { this . renderScrollSpy ( sections . forms ) }
222265 < FormSection />
223266
224- < ScrollSpy href = { sections . media } onSpy = { this . setActiveNavItem } />
267+ { this . renderScrollSpy ( sections . media ) }
225268 < div className = "bs-docs-section" >
226269 < h1 className = "page-header" >
227270 < Anchor id = "media-content" > Media content</ Anchor >
@@ -230,16 +273,16 @@ const ComponentsPage = React.createClass({
230273 < p className = "lead" > The React-Bootstrap media content components offer ways to present images and other media to your users in a responsive way, along with support for styling those components.</ p >
231274 </ div >
232275
233- < ScrollSpy href = { sections . images } onSpy = { this . setActiveNavItem } />
276+ { this . renderScrollSpy ( sections . images ) }
234277 < ImageSection />
235- < ScrollSpy href = { sections . thumbnails } onSpy = { this . setActiveNavItem } />
278+ { this . renderScrollSpy ( sections . thumbnails ) }
236279 < ThumbnailSection />
237- < ScrollSpy href = { sections . embed } onSpy = { this . setActiveNavItem } />
280+ { this . renderScrollSpy ( sections . embed ) }
238281 < ResponsiveEmbedSection />
239- < ScrollSpy href = { sections . carousels } onSpy = { this . setActiveNavItem } />
282+ { this . renderScrollSpy ( sections . carousels ) }
240283 < CarouselSection />
241284
242- < ScrollSpy href = { sections . misc } onSpy = { this . setActiveNavItem } />
285+ { this . renderScrollSpy ( sections . misc ) }
243286 < div className = "bs-docs-section" >
244287 < h1 className = "page-header" >
245288 < Anchor id = "misc" > Miscellaneous components</ Anchor >
@@ -248,19 +291,19 @@ const ComponentsPage = React.createClass({
248291 < p className = "lead" > React-Bootstrap also offers various standalone components that can be used to present specific, relevant kinds of information across your pages.</ p >
249292 </ div >
250293
251- < ScrollSpy href = { sections . icons } onSpy = { this . setActiveNavItem } />
294+ { this . renderScrollSpy ( sections . icons ) }
252295 < GlyphiconSection />
253- < ScrollSpy href = { sections . labels } onSpy = { this . setActiveNavItem } />
296+ { this . renderScrollSpy ( sections . labels ) }
254297 < LabelSection />
255- < ScrollSpy href = { sections . badges } onSpy = { this . setActiveNavItem } />
298+ { this . renderScrollSpy ( sections . badges ) }
256299 < BadgeSection />
257- < ScrollSpy href = { sections . alerts } onSpy = { this . setActiveNavItem } />
300+ { this . renderScrollSpy ( sections . alerts ) }
258301 < AlertsSection />
259- < ScrollSpy href = { sections . progress } onSpy = { this . setActiveNavItem } />
302+ { this . renderScrollSpy ( sections . progress ) }
260303 < ProgressBarSection />
261304
262305
263- < ScrollSpy href = { sections . utilities } onSpy = { this . setActiveNavItem } />
306+ { this . renderScrollSpy ( sections . utilities ) }
264307 < div className = "bs-docs-section" >
265308 < h1 className = "page-header" >
266309 < Anchor id = "utilities" > Utilities</ Anchor >
@@ -269,10 +312,10 @@ const ComponentsPage = React.createClass({
269312 < p className = "lead" > React-Bootstrap also exposes certain utility components used internally. They can be used to enhance your own custom components as well.</ p >
270313 </ div >
271314
272- < ScrollSpy href = { sections . transitions } onSpy = { this . setActiveNavItem } />
315+ { this . renderScrollSpy ( sections . transitions ) }
273316 < TransitionSection />
274317
275- < ScrollSpy href = { sections . missing } onSpy = { this . setActiveNavItem } />
318+ { this . renderScrollSpy ( sections . missing ) }
276319 < div className = "bs-docs-section" >
277320 < h1 className = "page-header" >
278321 < Anchor id = "missing" > Missing components</ Anchor >
@@ -281,7 +324,7 @@ const ComponentsPage = React.createClass({
281324 < p className = "lead" > We've intentionally omitted a few components from React-Bootstrap. Don't worry, though – we cover what to do in this section.</ p >
282325 </ div >
283326
284- < ScrollSpy href = { sections . affix } onSpy = { this . setActiveNavItem } />
327+ { this . renderScrollSpy ( sections . affix ) }
285328 < div className = "bs-docs-section" >
286329 < h2 className = "page-header" >
287330 < Anchor id = "affix" > Affix</ Anchor >
@@ -291,7 +334,7 @@ const ComponentsPage = React.createClass({
291334 < p > There isn't really any additional Bootstrap markup associated with affixes, so we didn't add a Bootstrap-specific affix class. The upstream ones already give you everything you need.</ p >
292335 </ div >
293336
294- < ScrollSpy href = { sections . scrollspy } onSpy = { this . setActiveNavItem } />
337+ { this . renderScrollSpy ( sections . scrollspy ) }
295338 < div className = "bs-docs-section" >
296339 < h2 className = "page-header" >
297340 < Anchor id = "scrollspy" > Scrollspy</ Anchor >
0 commit comments