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
186 lines (172 loc) • 5.03 kB
JavaScript
import { NAME_ALERT } from '../../constants/components'
import { EVENT_NAME_DISMISSED, EVENT_NAME_DISMISS_COUNT_DOWN } from '../../constants/events'
import {
PROP_TYPE_BOOLEAN,
PROP_TYPE_BOOLEAN_NUMBER_STRING,
PROP_TYPE_STRING
} from '../../constants/props'
import { SLOT_NAME_DISMISS } from '../../constants/slots'
import { normalizeSlotMixin } from '../../mixins/normalize-slot'
import { requestAF } from '../../utils/dom'
import { isBoolean, isNumeric } from '../../utils/inspect'
import { makeModelMixin } from '../../utils/model'
import { toInteger } from '../../utils/number'
import { sortKeys } from '../../utils/object'
import { makeProp, makePropsConfigurable } from '../../utils/props'
import { COMPONENT_UID_KEY, extend } from '../../vue'
import { BButtonClose } from '../button/button-close'
import { BVTransition } from '../transition/bv-transition'
// --- Constants ---
const {
mixin: modelMixin,
props: modelProps,
prop: MODEL_PROP_NAME,
event: MODEL_EVENT_NAME
} = makeModelMixin('show', {
type: PROP_TYPE_BOOLEAN_NUMBER_STRING,
defaultValue: false
})
// --- Helper methods ---
// Convert `show` value to a number
const parseCountDown = show => {
if (show === '' || isBoolean(show)) {
return 0
}
show = toInteger(show, 0)
return show > 0 ? show : 0
}
// Convert `show` value to a boolean
const parseShow = show => {
if (show === '' || show === true) {
return true
}
if (toInteger(show, 0) < 1) {
// Boolean will always return false for the above comparison
return false
}
return !!show
}
// --- Props ---
export const props = makePropsConfigurable(
sortKeys({
...modelProps,
dismissLabel: makeProp(PROP_TYPE_STRING, 'Close'),
dismissible: makeProp(PROP_TYPE_BOOLEAN, false),
fade: makeProp(PROP_TYPE_BOOLEAN, false),
variant: makeProp(PROP_TYPE_STRING, 'info')
}),
NAME_ALERT
)
// --- Main component ---
// @vue/component
export const BAlert = /*#__PURE__*/ extend({
name: NAME_ALERT,
mixins: [modelMixin, normalizeSlotMixin],
props,
data() {
return {
countDown: 0,
// If initially shown, we need to set these for SSR
localShow: parseShow(this[MODEL_PROP_NAME])
}
},
watch: {
[MODEL_PROP_NAME](newValue) {
this.countDown = parseCountDown(newValue)
this.localShow = parseShow(newValue)
},
countDown(newValue) {
this.clearCountDownInterval()
const show = this[MODEL_PROP_NAME]
// Ignore if `show` transitions to a boolean value
if (isNumeric(show)) {
this.$emit(EVENT_NAME_DISMISS_COUNT_DOWN, newValue)
// Update the v-model if needed
if (show !== newValue) {
this.$emit(MODEL_EVENT_NAME, newValue)
}
if (newValue > 0) {
this.localShow = true
this.$_countDownTimeout = setTimeout(() => {
this.countDown--
}, 1000)
} else {
// Slightly delay the hide to allow any UI updates
this.$nextTick(() => {
requestAF(() => {
this.localShow = false
})
})
}
}
},
localShow(newValue) {
const show = this[MODEL_PROP_NAME]
// Only emit dismissed events for dismissible or auto-dismissing alerts
if (!newValue && (this.dismissible || isNumeric(show))) {
this.$emit(EVENT_NAME_DISMISSED)
}
// Only emit booleans if we weren't passed a number via v-model
if (!isNumeric(show) && show !== newValue) {
this.$emit(MODEL_EVENT_NAME, newValue)
}
}
},
created() {
// Create private non-reactive props
this.$_filterTimer = null
const show = this[MODEL_PROP_NAME]
this.countDown = parseCountDown(show)
this.localShow = parseShow(show)
},
beforeDestroy() {
this.clearCountDownInterval()
},
methods: {
dismiss() {
this.clearCountDownInterval()
this.countDown = 0
this.localShow = false
},
clearCountDownInterval() {
clearTimeout(this.$_countDownTimeout)
this.$_countDownTimeout = null
}
},
render(h) {
let $alert = h()
if (this.localShow) {
const { dismissible, variant } = this
let $dismissButton = h()
if (dismissible) {
// Add dismiss button
$dismissButton = h(
BButtonClose,
{
attrs: { 'aria-label': this.dismissLabel },
on: { click: this.dismiss }
},
[this.normalizeSlot(SLOT_NAME_DISMISS)]
)
}
$alert = h(
'div',
{
staticClass: 'alert',
class: {
'alert-dismissible': dismissible,
[`alert-${variant}`]: variant
},
attrs: {
role: 'alert',
'aria-live': 'polite',
'aria-atomic': true
},
key: this[COMPONENT_UID_KEY]
},
[$dismissButton, this.normalizeSlot()]
)
}
return h(BVTransition, { props: { noFade: !this.fade } }, [$alert])
}
})