vOn.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import {
  2. transformOn as baseTransform,
  3. DirectiveTransform,
  4. createObjectProperty,
  5. createCallExpression,
  6. createSimpleExpression,
  7. NodeTypes,
  8. createCompoundExpression,
  9. ExpressionNode,
  10. SimpleExpressionNode,
  11. isStaticExp,
  12. CompilerDeprecationTypes,
  13. TransformContext,
  14. SourceLocation,
  15. checkCompatEnabled
  16. } from '@vue/compiler-core'
  17. import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../runtimeHelpers'
  18. import { makeMap, capitalize } from '@vue/shared'
  19. const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`)
  20. const isNonKeyModifier = /*#__PURE__*/ makeMap(
  21. // event propagation management
  22. `stop,prevent,self,` +
  23. // system modifiers + exact
  24. `ctrl,shift,alt,meta,exact,` +
  25. // mouse
  26. `middle`
  27. )
  28. // left & right could be mouse or key modifiers based on event type
  29. const maybeKeyModifier = /*#__PURE__*/ makeMap('left,right')
  30. const isKeyboardEvent = /*#__PURE__*/ makeMap(
  31. `onkeyup,onkeydown,onkeypress`,
  32. true
  33. )
  34. const resolveModifiers = (
  35. key: ExpressionNode,
  36. modifiers: string[],
  37. context: TransformContext,
  38. loc: SourceLocation
  39. ) => {
  40. const keyModifiers = []
  41. const nonKeyModifiers = []
  42. const eventOptionModifiers = []
  43. for (let i = 0; i < modifiers.length; i++) {
  44. const modifier = modifiers[i]
  45. if (
  46. __COMPAT__ &&
  47. modifier === 'native' &&
  48. checkCompatEnabled(
  49. CompilerDeprecationTypes.COMPILER_V_ON_NATIVE,
  50. context,
  51. loc
  52. )
  53. ) {
  54. eventOptionModifiers.push(modifier)
  55. } else if (isEventOptionModifier(modifier)) {
  56. // eventOptionModifiers: modifiers for addEventListener() options,
  57. // e.g. .passive & .capture
  58. eventOptionModifiers.push(modifier)
  59. } else {
  60. // runtimeModifiers: modifiers that needs runtime guards
  61. if (maybeKeyModifier(modifier)) {
  62. if (isStaticExp(key)) {
  63. if (isKeyboardEvent((key as SimpleExpressionNode).content)) {
  64. keyModifiers.push(modifier)
  65. } else {
  66. nonKeyModifiers.push(modifier)
  67. }
  68. } else {
  69. keyModifiers.push(modifier)
  70. nonKeyModifiers.push(modifier)
  71. }
  72. } else {
  73. if (isNonKeyModifier(modifier)) {
  74. nonKeyModifiers.push(modifier)
  75. } else {
  76. keyModifiers.push(modifier)
  77. }
  78. }
  79. }
  80. }
  81. return {
  82. keyModifiers,
  83. nonKeyModifiers,
  84. eventOptionModifiers
  85. }
  86. }
  87. const transformClick = (key: ExpressionNode, event: string) => {
  88. const isStaticClick =
  89. isStaticExp(key) && key.content.toLowerCase() === 'onclick'
  90. return isStaticClick
  91. ? createSimpleExpression(event, true)
  92. : key.type !== NodeTypes.SIMPLE_EXPRESSION
  93. ? createCompoundExpression([
  94. `(`,
  95. key,
  96. `) === "onClick" ? "${event}" : (`,
  97. key,
  98. `)`
  99. ])
  100. : key
  101. }
  102. export const transformOn: DirectiveTransform = (dir, node, context) => {
  103. return baseTransform(dir, node, context, baseResult => {
  104. const { modifiers } = dir
  105. if (!modifiers.length) return baseResult
  106. let { key, value: handlerExp } = baseResult.props[0]
  107. const { keyModifiers, nonKeyModifiers, eventOptionModifiers } =
  108. resolveModifiers(key, modifiers, context, dir.loc)
  109. // normalize click.right and click.middle since they don't actually fire
  110. if (nonKeyModifiers.includes('right')) {
  111. key = transformClick(key, `onContextmenu`)
  112. }
  113. if (nonKeyModifiers.includes('middle')) {
  114. key = transformClick(key, `onMouseup`)
  115. }
  116. if (nonKeyModifiers.length) {
  117. handlerExp = createCallExpression(context.helper(V_ON_WITH_MODIFIERS), [
  118. handlerExp,
  119. JSON.stringify(nonKeyModifiers)
  120. ])
  121. }
  122. if (
  123. keyModifiers.length &&
  124. // if event name is dynamic, always wrap with keys guard
  125. (!isStaticExp(key) || isKeyboardEvent(key.content))
  126. ) {
  127. handlerExp = createCallExpression(context.helper(V_ON_WITH_KEYS), [
  128. handlerExp,
  129. JSON.stringify(keyModifiers)
  130. ])
  131. }
  132. if (eventOptionModifiers.length) {
  133. const modifierPostfix = eventOptionModifiers.map(capitalize).join('')
  134. key = isStaticExp(key)
  135. ? createSimpleExpression(`${key.content}${modifierPostfix}`, true)
  136. : createCompoundExpression([`(`, key, `) + "${modifierPostfix}"`])
  137. }
  138. return {
  139. props: [createObjectProperty(key, handlerExp)]
  140. }
  141. })
  142. }