2021-04-12 19:43:53 -04:00

150 lines
4.1 KiB
TypeScript

import {
transformOn as baseTransform,
DirectiveTransform,
createObjectProperty,
createCallExpression,
createSimpleExpression,
NodeTypes,
createCompoundExpression,
ExpressionNode,
SimpleExpressionNode,
isStaticExp,
warnDeprecation,
CompilerDeprecationTypes
} from '@vue/compiler-core'
import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../runtimeHelpers'
import { makeMap, capitalize } from '@vue/shared'
const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`)
const isNonKeyModifier = /*#__PURE__*/ makeMap(
// event propagation management
`stop,prevent,self,` +
// system modifiers + exact
`ctrl,shift,alt,meta,exact,` +
// mouse
`middle`
)
// left & right could be mouse or key modifiers based on event type
const maybeKeyModifier = /*#__PURE__*/ makeMap('left,right')
const isKeyboardEvent = /*#__PURE__*/ makeMap(
`onkeyup,onkeydown,onkeypress`,
true
)
const resolveModifiers = (key: ExpressionNode, modifiers: string[]) => {
const keyModifiers = []
const nonKeyModifiers = []
const eventOptionModifiers = []
for (let i = 0; i < modifiers.length; i++) {
const modifier = modifiers[i]
if (isEventOptionModifier(modifier)) {
// eventOptionModifiers: modifiers for addEventListener() options,
// e.g. .passive & .capture
eventOptionModifiers.push(modifier)
} else {
// runtimeModifiers: modifiers that needs runtime guards
if (maybeKeyModifier(modifier)) {
if (isStaticExp(key)) {
if (isKeyboardEvent((key as SimpleExpressionNode).content)) {
keyModifiers.push(modifier)
} else {
nonKeyModifiers.push(modifier)
}
} else {
keyModifiers.push(modifier)
nonKeyModifiers.push(modifier)
}
} else {
if (isNonKeyModifier(modifier)) {
nonKeyModifiers.push(modifier)
} else {
keyModifiers.push(modifier)
}
}
}
}
return {
keyModifiers,
nonKeyModifiers,
eventOptionModifiers
}
}
const transformClick = (key: ExpressionNode, event: string) => {
const isStaticClick =
isStaticExp(key) && key.content.toLowerCase() === 'onclick'
return isStaticClick
? createSimpleExpression(event, true)
: key.type !== NodeTypes.SIMPLE_EXPRESSION
? createCompoundExpression([
`(`,
key,
`) === "onClick" ? "${event}" : (`,
key,
`)`
])
: key
}
export const transformOn: DirectiveTransform = (dir, node, context) => {
return baseTransform(dir, node, context, baseResult => {
const { modifiers } = dir
if (!modifiers.length) return baseResult
if (__COMPAT__ && __DEV__ && modifiers.includes('native')) {
warnDeprecation(
CompilerDeprecationTypes.V_ON_NATIVE_MODIFIER,
context,
dir.loc
)
}
let { key, value: handlerExp } = baseResult.props[0]
const {
keyModifiers,
nonKeyModifiers,
eventOptionModifiers
} = resolveModifiers(key, modifiers)
// normalize click.right and click.middle since they don't actually fire
if (nonKeyModifiers.includes('right')) {
key = transformClick(key, `onContextmenu`)
}
if (nonKeyModifiers.includes('middle')) {
key = transformClick(key, `onMouseup`)
}
if (nonKeyModifiers.length) {
handlerExp = createCallExpression(context.helper(V_ON_WITH_MODIFIERS), [
handlerExp,
JSON.stringify(nonKeyModifiers)
])
}
if (
keyModifiers.length &&
// if event name is dynamic, always wrap with keys guard
(!isStaticExp(key) || isKeyboardEvent(key.content))
) {
handlerExp = createCallExpression(context.helper(V_ON_WITH_KEYS), [
handlerExp,
JSON.stringify(keyModifiers)
])
}
if (eventOptionModifiers.length) {
const modifierPostfix = eventOptionModifiers.map(capitalize).join('')
key = isStaticExp(key)
? createSimpleExpression(`${key.content}${modifierPostfix}`, true)
: createCompoundExpression([`(`, key, `) + "${modifierPostfix}"`])
}
return {
props: [createObjectProperty(key, handlerExp)]
}
})
}