UNPKG

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

118 lines (106 loc) 3.56 kB
import KeyCodes from '../../utils/key-codes' import { eventOn, eventOff, getAttr, hasAttr, isDisabled, matches, select, setAttr } from '../../utils/dom' import { isString } from '../../utils/inspect' import { keys } from '../../utils/object' // Emitted show event for modal const EVENT_SHOW = 'bv::show::modal' // Prop name we use to store info on root element const PROPERTY = '__bv_modal_directive__' const EVENT_OPTS = { passive: true } const getTarget = ({ modifiers = {}, arg, value }) => { // Try value, then arg, otherwise pick last modifier return isString(value) ? value : isString(arg) ? arg : keys(modifiers).reverse()[0] } const getTriggerElement = el => { // If root element is a dropdown-item or nav-item, we // need to target the inner link or button instead return el && matches(el, '.dropdown-menu > li, li.nav-item') ? select('a, button', el) || el : el } const setRole = trigger => { // Ensure accessibility on non button elements if (trigger && trigger.tagName !== 'BUTTON') { // Only set a role if the trigger element doesn't have one if (!hasAttr(trigger, 'role')) { setAttr(trigger, 'role', 'button') } // Add a tabindex is not a button or link, and tabindex is not provided if (trigger.tagName !== 'A' && !hasAttr(trigger, 'tabindex')) { setAttr(trigger, 'tabindex', '0') } } } const bind = (el, binding, vnode) => { const target = getTarget(binding) const trigger = getTriggerElement(el) if (target && trigger) { const handler = evt => { // `currentTarget` is the element with the listener on it const currentTarget = evt.currentTarget if (!isDisabled(currentTarget)) { const type = evt.type const key = evt.keyCode // Open modal only if trigger is not disabled if ( type === 'click' || (type === 'keydown' && (key === KeyCodes.ENTER || key === KeyCodes.SPACE)) ) { vnode.context.$root.$emit(EVENT_SHOW, target, currentTarget) } } } el[PROPERTY] = { handler, target, trigger } // If element is not a button, we add `role="button"` for accessibility setRole(trigger) // Listen for click events eventOn(trigger, 'click', handler, EVENT_OPTS) if (trigger.tagName !== 'BUTTON' && getAttr(trigger, 'role') === 'button') { // If trigger isn't a button but has role button, // we also listen for `keydown.space` && `keydown.enter` eventOn(trigger, 'keydown', handler, EVENT_OPTS) } } } const unbind = el => { const oldProp = el[PROPERTY] || {} const trigger = oldProp.trigger const handler = oldProp.handler if (trigger && handler) { eventOff(trigger, 'click', handler, EVENT_OPTS) eventOff(trigger, 'keydown', handler, EVENT_OPTS) eventOff(el, 'click', handler, EVENT_OPTS) eventOff(el, 'keydown', handler, EVENT_OPTS) } delete el[PROPERTY] } const componentUpdated = (el, binding, vnode) => { const oldProp = el[PROPERTY] || {} const target = getTarget(binding) const trigger = getTriggerElement(el) if (target !== oldProp.target || trigger !== oldProp.trigger) { // We bind and rebind if the target or trigger changes unbind(el, binding, vnode) bind(el, binding, vnode) } // If trigger element is not a button, ensure `role="button"` // is still set for accessibility setRole(trigger) } const updated = () => {} /* * Export our directive */ export const VBModal = { inserted: componentUpdated, updated, componentUpdated, unbind }