🌐 AI搜索 & 代理 主页
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 82 additions & 27 deletions src/directives/modal/modal.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,94 @@
import { setAttr, removeAttr } from '../../utils/dom'
import { bindTargets, unbindTargets } from '../../utils/target'

// Target listen types
const listenTypes = { click: true }
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'

const setRole = (el, binding, vnode) => {
if (el.tagName !== 'BUTTON') {
setAttr(el, 'role', 'button')
// Prop name we use to store info on root element
const HANDLER = '__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 => {
// Only set a role if the trigger element doesn't have one
if (trigger && trigger.tagName !== 'BUTTON' && !hasAttr(trigger, 'role')) {
setAttr(trigger, 'role', 'button')
}
}

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
// Open modal only if trigger is not disabled
if (type === 'click' || (type === 'keydown' && evt.keyCode === 32)) {
vnode.context.$root.$emit(EVENT_SHOW, target, currentTarget)
}
}
}
el[HANDLER] = handler
// 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`
eventOn(trigger, 'keydown', handler, EVENT_OPTS)
}
}
}

const unbind = el => {
const trigger = getTriggerElement(el)
const handler = el ? el[HANDLER] : null
if (trigger && handler) {
eventOff(trigger, 'click', handler, EVENT_OPTS)
eventOff(trigger, 'keydown', handler, EVENT_OPTS)
}
delete el[HANDLER]
}

const componentUpdated = (el, binding, vnode) => {
// We bind and rebind just in case target changes
unbind(el, binding, vnode)
bind(el, binding, vnode)
}

const updated = () => {}

/*
* Export our directive
*/
export const VBModal = {
// eslint-disable-next-line no-shadow-restricted-names
bind(el, binding, vnode) {
bindTargets(vnode, binding, listenTypes, ({ targets, vnode }) => {
targets.forEach(target => {
vnode.context.$root.$emit(EVENT_SHOW, target, vnode.elm)
})
})
// If element is not a button, we add `role="button"` for accessibility
setRole(el, binding, vnode)
},
updated: setRole,
componentUpdated: setRole,
unbind(el, binding, vnode) {
unbindTargets(vnode, binding, listenTypes)
// If element is not a button, we add `role="button"` for accessibility
if (el.tagName !== 'BUTTON') {
removeAttr(el, 'role', 'button')
}
}
inserted: componentUpdated,
updated,
componentUpdated,
unbind
}