bootstrap-vue
Version:
With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens
118 lines (107 loc) • 3.47 kB
JavaScript
import { IS_BROWSER } from '../../constants/env'
import { isNumber, isObject, isString } from '../../utils/inspect'
import { mathRound } from '../../utils/math'
import { toInteger } from '../../utils/number'
import { keys } from '../../utils/object'
import { getEventRoot } from '../../utils/get-event-root'
import { getInstanceFromDirective } from '../../utils/get-instance-from-directive'
import { BVScrollspy } from './helpers/bv-scrollspy.class'
// Key we use to store our instance
const BV_SCROLLSPY = '__BV_Scrollspy__'
// Pre-compiled regular expressions
const onlyDigitsRE = /^\d+$/
const offsetRE = /^(auto|position|offset)$/
// Build a Scrollspy config based on bindings (if any)
// Arguments and modifiers take precedence over passed value config object
/* istanbul ignore next: not easy to test */
const parseBindings = bindings => /* istanbul ignore next: not easy to test */ {
const config = {}
// If argument, assume element ID
if (bindings.arg) {
// Element ID specified as arg
// We must prepend '#' to become a CSS selector
config.element = `#${bindings.arg}`
}
// Process modifiers
keys(bindings.modifiers).forEach(mod => {
if (onlyDigitsRE.test(mod)) {
// Offset value
config.offset = toInteger(mod, 0)
} else if (offsetRE.test(mod)) {
// Offset method
config.method = mod
}
})
// Process value
if (isString(bindings.value)) {
// Value is a CSS ID or selector
config.element = bindings.value
} else if (isNumber(bindings.value)) {
// Value is offset
config.offset = mathRound(bindings.value)
} else if (isObject(bindings.value)) {
// Value is config object
// Filter the object based on our supported config options
keys(bindings.value)
.filter(k => !!BVScrollspy.DefaultType[k])
.forEach(k => {
config[k] = bindings.value[k]
})
}
return config
}
// Add or update Scrollspy on our element
const applyScrollspy = (el, bindings, vnode) => /* istanbul ignore next: not easy to test */ {
if (!IS_BROWSER) {
/* istanbul ignore next */
return
}
const config = parseBindings(bindings)
if (el[BV_SCROLLSPY]) {
el[BV_SCROLLSPY].updateConfig(config, getEventRoot(getInstanceFromDirective(vnode, bindings)))
} else {
el[BV_SCROLLSPY] = new BVScrollspy(
el,
config,
getEventRoot(getInstanceFromDirective(vnode, bindings))
)
}
}
// Remove Scrollspy on our element
/* istanbul ignore next: not easy to test */
const removeScrollspy = el => /* istanbul ignore next: not easy to test */ {
if (el[BV_SCROLLSPY]) {
el[BV_SCROLLSPY].dispose()
el[BV_SCROLLSPY] = null
delete el[BV_SCROLLSPY]
}
}
/*
* Export our directive
*/
export const VBScrollspy = {
/* istanbul ignore next: not easy to test */
bind(el, bindings, vnode) {
applyScrollspy(el, bindings, vnode)
},
/* istanbul ignore next: not easy to test */
inserted(el, bindings, vnode) {
applyScrollspy(el, bindings, vnode)
},
/* istanbul ignore next: not easy to test */
update(el, bindings, vnode) {
if (bindings.value !== bindings.oldValue) {
applyScrollspy(el, bindings, vnode)
}
},
/* istanbul ignore next: not easy to test */
componentUpdated(el, bindings, vnode) {
if (bindings.value !== bindings.oldValue) {
applyScrollspy(el, bindings, vnode)
}
},
/* istanbul ignore next: not easy to test */
unbind(el) {
removeScrollspy(el)
}
}