]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-vapor): support keys and nonKeys modifier for component event (#13053)
authorzhiyuanzmj <260480378@qq.com>
Mon, 20 Oct 2025 07:21:19 +0000 (15:21 +0800)
committerGitHub <noreply@github.com>
Mon, 20 Oct 2025 07:21:19 +0000 (15:21 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
packages/compiler-vapor/src/generators/component.ts
packages/compiler-vapor/src/generators/prop.ts
packages/compiler-vapor/src/transform.ts
packages/compiler-vapor/src/transforms/vOn.ts

index 2b9ed3bdbb09473450246bd80d52410425b4bb55..6f92a4c1bf7b0c9a9c0593702d565e7b6c811877 100644 (file)
@@ -246,6 +246,36 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler: element transform > component event with keys modifier 1`] = `
+"import { resolveComponent as _resolveComponent, withKeys as _withKeys, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+  const _component_Foo = _resolveComponent("Foo")
+  const n0 = _createComponentWithFallback(_component_Foo, { onKeyup: () => _withKeys(_ctx.bar, ["enter"]) }, null, true)
+  return n0
+}"
+`;
+
+exports[`compiler: element transform > component event with multiple modifiers and event options 1`] = `
+"import { resolveComponent as _resolveComponent, withModifiers as _withModifiers, withKeys as _withKeys, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+  const _component_Foo = _resolveComponent("Foo")
+  const n0 = _createComponentWithFallback(_component_Foo, { onFooCaptureOnce: () => _withKeys(_withModifiers(_ctx.bar, ["stop","prevent"]), ["enter"]) }, null, true)
+  return n0
+}"
+`;
+
+exports[`compiler: element transform > component event with nonKeys modifier 1`] = `
+"import { resolveComponent as _resolveComponent, withModifiers as _withModifiers, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+  const _component_Foo = _resolveComponent("Foo")
+  const n0 = _createComponentWithFallback(_component_Foo, { onFoo: () => _withModifiers(_ctx.bar, ["stop","prevent"]) }, null, true)
+  return n0
+}"
+`;
+
 exports[`compiler: element transform > component event with once modifier 1`] = `
 "import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
index 20f6005ffe7bb93bb2ad37b65b485bf56e377d78..c20ca11614e1af2240669bea073177537a07a4da 100644 (file)
@@ -896,6 +896,78 @@ describe('compiler: element transform', () => {
     })
   })
 
+  test('component event with keys modifier', () => {
+    const { code, ir } = compileWithElementTransform(
+      `<Foo @keyup.enter="bar" />`,
+    )
+    expect(code).toMatchSnapshot()
+    expect(ir.block.dynamic.children[0].operation).toMatchObject({
+      type: IRNodeTypes.CREATE_COMPONENT_NODE,
+      tag: 'Foo',
+      props: [
+        [
+          {
+            key: { content: 'keyup' },
+            handler: true,
+            handlerModifiers: {
+              keys: ['enter'],
+              nonKeys: [],
+              options: [],
+            },
+          },
+        ],
+      ],
+    })
+  })
+
+  test('component event with nonKeys modifier', () => {
+    const { code, ir } = compileWithElementTransform(
+      `<Foo @foo.stop.prevent="bar" />`,
+    )
+    expect(code).toMatchSnapshot()
+    expect(ir.block.dynamic.children[0].operation).toMatchObject({
+      type: IRNodeTypes.CREATE_COMPONENT_NODE,
+      tag: 'Foo',
+      props: [
+        [
+          {
+            key: { content: 'foo' },
+            handler: true,
+            handlerModifiers: {
+              keys: [],
+              nonKeys: ['stop', 'prevent'],
+              options: [],
+            },
+          },
+        ],
+      ],
+    })
+  })
+
+  test('component event with multiple modifiers and event options', () => {
+    const { code, ir } = compileWithElementTransform(
+      `<Foo @foo.enter.stop.prevent.capture.once="bar" />`,
+    )
+    expect(code).toMatchSnapshot()
+    expect(ir.block.dynamic.children[0].operation).toMatchObject({
+      type: IRNodeTypes.CREATE_COMPONENT_NODE,
+      tag: 'Foo',
+      props: [
+        [
+          {
+            key: { content: 'foo' },
+            handler: true,
+            handlerModifiers: {
+              keys: ['enter'],
+              nonKeys: ['stop', 'prevent'],
+              options: ['capture', 'once'],
+            },
+          },
+        ],
+      ],
+    })
+  })
+
   test('component with dynamic event arguments', () => {
     const { code, ir } = compileWithElementTransform(
       `<Foo @[foo-bar]="bar" @[baz]="qux" />`,
index 41d04c83d5dbd541d4590f6eb8c4779a12d2e437..04df6c5a48a3804766502bb1b4d4f41c27904b6e 100644 (file)
@@ -222,7 +222,7 @@ function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) {
       ? genEventHandler(
           context,
           prop.values[0],
-          undefined,
+          prop.handlerModifiers,
           true /* wrap handlers passed to components */,
         )
       : isStatic
index 42f063331fc1090ffc8669b04a54490e234ef2da..e486cffeb56940fe66733bcd02aad4417c66d2ea 100644 (file)
@@ -114,9 +114,10 @@ export function genPropKey(
 ): CodeFragment[] {
   const { helper } = context
 
-  const handlerModifierPostfix = handlerModifiers
-    ? handlerModifiers.map(capitalize).join('')
-    : ''
+  const handlerModifierPostfix =
+    handlerModifiers && handlerModifiers.options
+      ? handlerModifiers.options.map(capitalize).join('')
+      : ''
   // static arg was transformed by v-bind transformer
   if (node.isStatic) {
     // only quote keys if necessary
index b79152f37b6dd88089ab5df225dc2413f5be1f3d..8c5ca54d2b311296acab92c5e290ebc9b87e9378 100644 (file)
@@ -24,6 +24,7 @@ import {
   type IRSlots,
   type OperationNode,
   type RootIRNode,
+  type SetEventIRNode,
   type VaporDirectiveNode,
 } from './ir'
 import { isConstantExpression, isStaticExpression } from './utils'
@@ -46,7 +47,7 @@ export interface DirectiveTransformResult {
   modifier?: '.' | '^'
   runtimeCamelize?: boolean
   handler?: boolean
-  handlerModifiers?: string[]
+  handlerModifiers?: SetEventIRNode['modifiers']
   model?: boolean
   modelModifiers?: string[]
 }
index fcbfc265d43d710ba0f4299c13e77d5febc0d009..fe63ece0a88851ff8b34c6daa6944fb961ce50e5 100644 (file)
@@ -65,7 +65,11 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => {
       key: arg,
       value: handler,
       handler: true,
-      handlerModifiers: eventOptionModifiers,
+      handlerModifiers: {
+        keys: keyModifiers,
+        nonKeys: nonKeyModifiers,
+        options: eventOptionModifiers,
+      },
     }
   }