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)] } }) }