Permalink
Browse files

fix(scrollspy): Make work with new nav-link functional component (#909)

* fix(scrollspy): Make work with new nav-link functional component

Before, elements with class `.nav-link` had a compoent VM on their parent element, but `nav-link`s no longer have  associated VMs, so we set the `active` class directly on them.

* Update scrollspy.js

* Update README.md

* Update README.md

* [scrollspy] get $root from vnode context

* Update README.md
  • Loading branch information...
tmorehouse committed Aug 22, 2017
1 parent 5bd2b23 commit 2ebba5d90cf09bfe1454f742bff2de16fbb65df8
Showing with 36 additions and 24 deletions.
  1. +23 −8 docs/directives/scrollspy/README.md
  2. +13 −16 lib/directives/scrollspy.js
@@ -1,15 +1,17 @@
# ScrollSpy
> Directive `v-b-scrollspy` applied to the `<b-nav>` or `<b-navbar>` element(s) that
you want to have nav-links shown as `active` based on the scrolling of another
> Directive `v-b-scrollspy` is applied to the `<b-nav>`, `<b-navbar>`, actionable
`<b-list-group>` components, or HTML elements which contain that these components,
that you want to have links shown as `active` based on the scrolling of another
element (i.e. `<body>`).
**Note:** `v-b-scrollspy` directive is currently in the experimental stage.
**Example: scrolling on b-card body, with both b-nav and b-list-group components**
```html
<template>
<b-card>
<b-nav pills slot="header" v-b-scrollspy:scrollspy-example>
<b-card no-body>
<b-nav pills slot="header" v-b-scrollspy:example-scroller>
<b-nav-item href="#fat" @click="scrollIntoView">@fat</b-nav-item>
<b-nav-item href="#mdo" @click="scrollIntoView">@mdo</b-nav-item>
<b-nav-item-dropdown text="Dropdown 1,2,3" right-alignment>
@@ -20,7 +22,18 @@ element (i.e. `<body>`).
</b-nav-item-dropdown>
<b-nav-item href="#pi0" @click="scrollIntoView">@pi0</b-nav-item>
</b-nav>
<div id="scrollspy-example"
<b-list-group flush v-b-scrollspy:example-scroller>
<b-list-group-item href="#one" @click="scrollIntoView">
One
</b-list-group-item>
<b-list-group-item href="#two" @click="scrollIntoView">
Two
</b-list-group-item>
<b-list-group-item href="#three" @click="scrollIntoView">
Three
</b-list-group-item>
</b-list-group>
<b-card-body id="example-scroller"
ref="content"
style="position:relative; height:300px; overflow-y:scroll;">
<p>
@@ -72,7 +85,7 @@ element (i.e. `<body>`).
four loko nisi, ea helvetica nulla carles. Tattooed cosby sweater
food truck, mcsweeney's quis non freegan vinyl.
</p>
</div>
</b-card-body>
</b-card>
</template>
@@ -98,9 +111,11 @@ export default {
**Note:** The directive is applied backwards compared to native Bootstrap V4.
In **Bootstrap-Vue** the `v-b-scrollspy` directive is applied to the target
element that has the nav-links, and the option(s) specify the element to
element that has the nav-links, and the options specify which element to
monitor scrolling on.
The directive an be applied to any containing element or component.
### Usage
Assume `<body>` is the scroll element, and use default offset of 10 pixels
```html
@@ -143,14 +143,15 @@ function typeCheckConfig(componentName, config, configTypes) {
* ScrollSpy Class
*/
function ScrollSpy(el, binding) {
function ScrollSpy(el, binding, vnode) {
// The element that contains the nav-links et al
this._$el = el;
// The selectors to find the nav-links
// Thes are all functional components now
this._selector = [
Selector.NAV_LINKS,
Selector.LIST_ITEMS,
Selector.DROPDOWN_ITEMS
Selector.DROPDOWN_ITEMS
].join(',');
// Start off with default configurtion
this._config = assign({}, Default);
@@ -162,7 +163,7 @@ function ScrollSpy(el, binding) {
// Curent scroll height (for detecting document height changes)
this._scrollHeight = 0;
// Reference to the $root VM so we can spew events
this._$root = null;
this._$root = vnode.context.$root || null;
// Reference to our throttled resize timeout
this._resizeTimeout = null;
@@ -210,9 +211,9 @@ ScrollSpy.prototype.updateConfig = function (binding) {
// Check the config and log error to console. Unknown options are ignored
typeCheckConfig(NAME, this._config, DefaultType);
// Get Vue instance from element
// Get Vue root instance from element if needed
const vm = getVm(this._$el);
if (vm && vm.$root) {
if (!this._$root && vm && vm.$root) {
this._$root = vm.$root;
}
@@ -499,10 +500,6 @@ ScrollSpy.prototype._clear = function () {
// Else fallback to adding the active class
ScrollSpy.prototype._setActiveState = function (el, state) {
if (el) {
if (el.classList.contains(ClassName.NAV_LINK) && !el.classList.contains(ClassName.DROPDOWN_TOGGLE)) {
// Special case where VM with 'active' prop is on parent element
el = el.parentElement;
}
const vm = getVm(el);
if (vm && Object.prototype.hasOwnProperty.call(vm.$props, 'active')) {
// This is a component that has an `active` prop
@@ -541,42 +538,42 @@ ScrollSpy.prototype._setParentsSiblingActiveState = function (element, selector,
*/
export default {
bind(el, binding) {
bind(el, binding, vnode) {
if (isServer || el[BVSS]) {
return;
}
el[BVSS] = new ScrollSpy(el, binding);
},
inserted(el, binding) {
inserted(el, binding, vnode) {
if (isServer) {
return;
}
if (!el[BVSS]) {
el[BVSS] = new ScrollSpy(el, binding);
el[BVSS] = new ScrollSpy(el, binding, vnode);
el[BVSS].listen();
} else {
el[BVSS].updateConfig(binding).listen();
}
el[BVSS].refresh().process().scheduleRefresh();
},
update(el, binding) {
update(el, binding, vnode) {
if (isServer) {
return;
}
if (!el[BVSS]) {
el[BVSS] = new ScrollSpy(el, binding);
el[BVSS] = new ScrollSpy(el, binding, vnode);
el[BVSS].listen();
} else {
el[BVSS].updateConfig(binding);
}
el[BVSS].refresh().process().scheduleRefresh();
},
componentUpdated(el, binding) {
componentUpdated(el, binding, vnode) {
if (isServer) {
return;
}
if (!el[BVSS]) {
el[BVSS] = new ScrollSpy(el, binding);
el[BVSS] = new ScrollSpy(el, binding, vnode);
el[BVSS].listen();
} else {
el[BVSS].updateConfig(binding);

0 comments on commit 2ebba5d

Please sign in to comment.