bootstrap-vue
Version:
BootstrapVue, with over 40 plugins and more than 80 custom components, custom directives, and over 300 icons, provides one of the most comprehensive implementations of Bootstrap v4 components and grid system for Vue.js. With extensive and automated WAI-AR
216 lines (211 loc) • 5.08 kB
JavaScript
import Vue from '../../utils/vue'
import identity from '../../utils/identity'
import { concat } from '../../utils/array'
import { getComponentConfig } from '../../utils/config'
import { hasIntersectionObserverSupport } from '../../utils/env'
import { toInteger } from '../../utils/number'
import { VBVisible } from '../../directives/visible/visible'
import { BImg } from './img'
const NAME = 'BImgLazy'
export const props = {
src: {
type: String,
default: null,
required: true
},
srcset: {
type: [String, Array],
default: null
},
sizes: {
type: [String, Array],
default: null
},
alt: {
type: String,
default: null
},
width: {
type: [Number, String],
default: null
},
height: {
type: [Number, String],
default: null
},
blankSrc: {
// If null, a blank image is generated
type: String,
default: null
},
blankColor: {
type: String,
default: () => getComponentConfig(NAME, 'blankColor')
},
blankWidth: {
type: [Number, String],
default: null
},
blankHeight: {
type: [Number, String],
default: null
},
show: {
type: Boolean,
default: false
},
fluid: {
type: Boolean,
default: false
},
fluidGrow: {
type: Boolean,
default: false
},
block: {
type: Boolean,
default: false
},
thumbnail: {
type: Boolean,
default: false
},
rounded: {
type: [Boolean, String],
default: false
},
left: {
type: Boolean,
default: false
},
right: {
type: Boolean,
default: false
},
center: {
type: Boolean,
default: false
},
offset: {
// Distance away from viewport (in pixels) before being
// considered "visible"
type: [Number, String],
default: 360
}
}
// @vue/component
export const BImgLazy = /*#__PURE__*/ Vue.extend({
name: NAME,
directives: {
bVisible: VBVisible
},
props,
data() {
return {
isShown: this.show
}
},
computed: {
computedSrc() {
return !this.blankSrc || this.isShown ? this.src : this.blankSrc
},
computedBlank() {
return !(this.isShown || this.blankSrc)
},
computedWidth() {
return this.isShown ? this.width : this.blankWidth || this.width
},
computedHeight() {
return this.isShown ? this.height : this.blankHeight || this.height
},
computedSrcset() {
const srcset = concat(this.srcset)
.filter(identity)
.join(',')
return !this.blankSrc || this.isShown ? srcset : null
},
computedSizes() {
const sizes = concat(this.sizes)
.filter(identity)
.join(',')
return !this.blankSrc || this.isShown ? sizes : null
}
},
watch: {
show(newVal, oldVal) {
if (newVal !== oldVal) {
// If IntersectionObserver support is not available, image is always shown
const visible = hasIntersectionObserverSupport ? newVal : true
this.isShown = visible
if (visible !== newVal) {
// Ensure the show prop is synced (when no IntersectionObserver)
this.$nextTick(this.updateShowProp)
}
}
},
isShown(newVal, oldVal) {
if (newVal !== oldVal) {
// Update synched show prop
this.updateShowProp()
}
}
},
mounted() {
// If IntersectionObserver is not available, image is always shown
this.isShown = hasIntersectionObserverSupport ? this.show : true
},
methods: {
updateShowProp() {
this.$emit('update:show', this.isShown)
},
doShow(visible) {
// If IntersectionObserver is not supported, the callback
// will be called with `null` rather than `true` or `false`
if ((visible || visible === null) && !this.isShown) {
this.isShown = true
}
}
},
render(h) {
const directives = []
if (!this.isShown) {
// We only add the visible directive if we are not shown
directives.push({
// Visible directive will silently do nothing if
// IntersectionObserver is not supported
name: 'b-visible',
// Value expects a callback (passed one arg of `visible` = `true` or `false`)
value: this.doShow,
modifiers: {
// Root margin from viewport
[`${toInteger(this.offset) || 0}`]: true,
// Once the image is shown, stop observing
once: true
}
})
}
return h(BImg, {
directives,
props: {
// Computed value props
src: this.computedSrc,
blank: this.computedBlank,
width: this.computedWidth,
height: this.computedHeight,
srcset: this.computedSrcset || null,
sizes: this.computedSizes || null,
// Passthrough props
alt: this.alt,
blankColor: this.blankColor,
fluid: this.fluid,
fluidGrow: this.fluidGrow,
block: this.block,
thumbnail: this.thumbnail,
rounded: this.rounded,
left: this.left,
right: this.right,
center: this.center
}
})
}
})