Skip to content

Commit 0b263f7

Browse files
committed
Make the scrollspy work a bit better
1 parent 28146f7 commit 0b263f7

1 file changed

Lines changed: 102 additions & 59 deletions

File tree

docs/src/ComponentsPage.js

Lines changed: 102 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react';
2-
import findIndex from 'lodash-compat/array/findIndex';
32
import AutoAffix from 'react-overlays/lib/AutoAffix';
43
import 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 &ndash; 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

Comments
 (0)