From 7c082e1cfbcd52b9958c8fae99cba644d69b9c2a Mon Sep 17 00:00:00 2001
From: unknown <1987250181@qq.com>
Date: Mon, 15 Dec 2025 23:22:45 +0800
Subject: [PATCH] feat(vue-renderless/drawer): add close-on-press-escape api
---
examples/sites/demos/apis/drawer.js | 14 +++++++
.../close-on-press-escape-composition-api.vue | 29 ++++++++++++++
.../app/drawer/close-on-press-escape.spec.ts | 19 ++++++++++
.../pc/app/drawer/close-on-press-escape.vue | 38 +++++++++++++++++++
.../demos/pc/app/drawer/webdoc/drawer.js | 12 ++++++
packages/renderless/src/drawer/index.ts | 30 +++++++++++++++
packages/renderless/src/drawer/vue.ts | 14 +++++--
packages/vue/src/drawer/src/pc.vue | 3 +-
8 files changed, 155 insertions(+), 4 deletions(-)
create mode 100644 examples/sites/demos/pc/app/drawer/close-on-press-escape-composition-api.vue
create mode 100644 examples/sites/demos/pc/app/drawer/close-on-press-escape.spec.ts
create mode 100644 examples/sites/demos/pc/app/drawer/close-on-press-escape.vue
diff --git a/examples/sites/demos/apis/drawer.js b/examples/sites/demos/apis/drawer.js
index ac687e0873..bb7232dd93 100644
--- a/examples/sites/demos/apis/drawer.js
+++ b/examples/sites/demos/apis/drawer.js
@@ -207,6 +207,20 @@ export default {
mode: ['pc'],
pcDemo: 'tips-props',
hideSaas: true
+ },
+ {
+ name: 'close-on-press-escape',
+ type: 'boolean',
+ defaultValue: 'false',
+ desc: {
+ 'zh-CN': 'ESC 键关闭抽屉',
+ 'en-US': 'ESC key to close drawer'
+ },
+ mode: ['pc'],
+ pcDemo: 'closeOnPressEscape',
+ meta: {
+ stable: '3.28.0'
+ }
}
],
events: [
diff --git a/examples/sites/demos/pc/app/drawer/close-on-press-escape-composition-api.vue b/examples/sites/demos/pc/app/drawer/close-on-press-escape-composition-api.vue
new file mode 100644
index 0000000000..dbcaedd7e3
--- /dev/null
+++ b/examples/sites/demos/pc/app/drawer/close-on-press-escape-composition-api.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/examples/sites/demos/pc/app/drawer/close-on-press-escape.spec.ts b/examples/sites/demos/pc/app/drawer/close-on-press-escape.spec.ts
new file mode 100644
index 0000000000..2c91806dc7
--- /dev/null
+++ b/examples/sites/demos/pc/app/drawer/close-on-press-escape.spec.ts
@@ -0,0 +1,19 @@
+import { test, expect } from '@playwright/test'
+
+test('按 Esc 键关闭 Drawer(close-on-press-escape)', async ({ page }) => {
+ page.on('pageerror', (exception) => expect(exception).toBeNull())
+
+ await page.goto('drawer#close-on-press-escape')
+
+ const drawer = page.locator('.tiny-drawer__main')
+
+ // 打开 Drawer(用文本更稳定)
+ await page.getByText('抽屉组件').click()
+ await expect(drawer).toBeVisible()
+
+ // 按 Esc
+ await page.keyboard.press('Escape')
+
+ // Drawer 关闭
+ await expect(drawer).toBeHidden()
+})
diff --git a/examples/sites/demos/pc/app/drawer/close-on-press-escape.vue b/examples/sites/demos/pc/app/drawer/close-on-press-escape.vue
new file mode 100644
index 0000000000..640c1c82f0
--- /dev/null
+++ b/examples/sites/demos/pc/app/drawer/close-on-press-escape.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
diff --git a/examples/sites/demos/pc/app/drawer/webdoc/drawer.js b/examples/sites/demos/pc/app/drawer/webdoc/drawer.js
index 25a14d78ff..446969ced3 100644
--- a/examples/sites/demos/pc/app/drawer/webdoc/drawer.js
+++ b/examples/sites/demos/pc/app/drawer/webdoc/drawer.js
@@ -16,6 +16,18 @@ export default {
},
codeFiles: ['basic-usage.vue']
},
+ {
+ demoId: 'close-on-press-escape',
+ name: {
+ 'zh-CN': '按下 ESC 关闭抽屉',
+ 'en-US': ''
+ },
+ desc: {
+ 'zh-CN': '
添加 close-on-press-escape 属性可以控制是否可以通过 ESC 关闭抽屉。
',
+ 'en-US': ''
+ },
+ codeFiles: ['close-on-press-escape.vue']
+ },
{
demoId: 'use-through-method',
name: { 'zh-CN': '通过方法调用', 'en-US': 'Use through method' },
diff --git a/packages/renderless/src/drawer/index.ts b/packages/renderless/src/drawer/index.ts
index 48bcbf5ca6..b7a0e89b91 100644
--- a/packages/renderless/src/drawer/index.ts
+++ b/packages/renderless/src/drawer/index.ts
@@ -80,6 +80,36 @@ export const handleClose =
}
}
+/* ================= Esc 关闭(受控) ================= */
+export const keydown =
+ ({ api, state, props }: Pick) =>
+ (event: KeyboardEvent) => {
+ if (!state.visible) {
+ return
+ }
+
+ if (!props.closeOnPressEscape) {
+ return
+ }
+
+ if (event.key === 'Escape' || event.key === 'Esc') {
+ api.handleClose('esc', true)
+ }
+ }
+
+export const addKeydownEvent =
+ ({ api }: { api: IDrawerApi }) =>
+ () => {
+ document.addEventListener('keydown', api.keydown)
+ }
+
+export const removeKeydownEvent =
+ ({ api }: { api: IDrawerApi }) =>
+ () => {
+ document.removeEventListener('keydown', api.keydown)
+ }
+/* ================================================== */
+
export const mousedown =
({ state, vm }: { vm: ISharedRenderlessParamUtils['vm']; state: IDrawerState }) =>
(event) => {
diff --git a/packages/renderless/src/drawer/vue.ts b/packages/renderless/src/drawer/vue.ts
index ca457bfa3f..78974b043f 100644
--- a/packages/renderless/src/drawer/vue.ts
+++ b/packages/renderless/src/drawer/vue.ts
@@ -12,7 +12,10 @@ import {
handleClose,
computedWidth,
computedHeight,
- open
+ open,
+ keydown,
+ addKeydownEvent,
+ removeKeydownEvent
} from './index'
import type {
IDrawerProps,
@@ -50,8 +53,11 @@ export const renderless = (
mousedown: mousedown({ state, vm }),
mousemove: mousemove({ state, props, emit }),
mouseup: mouseup({ state }),
- addDragEvent: addDragEvent({ api: api as IDrawerApi, vm }),
- removeDragEvent: removeDragEvent({ api: api as IDrawerApi, vm }),
+ keydown: keydown({ api, state, props }),
+ addKeydownEvent: addKeydownEvent({ api }),
+ removeKeydownEvent: removeKeydownEvent({ api }),
+ addDragEvent: addDragEvent({ api, vm }),
+ removeDragEvent: removeDragEvent({ api, vm }),
watchVisible: watchVisible({ state, api }),
showScrollbar: showScrollbar(lockScrollClass),
hideScrollbar: hideScrollbar(lockScrollClass),
@@ -61,6 +67,7 @@ export const renderless = (
onMounted(() => {
props.dragable && api.addDragEvent()
+ api.addKeydownEvent()
if (props.lockScroll && props.visible) {
api.showScrollbar()
}
@@ -68,6 +75,7 @@ export const renderless = (
onBeforeUnmount(() => {
props.dragable && api.removeDragEvent()
+ api.removeKeydownEvent()
props.lockScroll && api.hideScrollbar()
})
diff --git a/packages/vue/src/drawer/src/pc.vue b/packages/vue/src/drawer/src/pc.vue
index 909f116606..238449a1c7 100644
--- a/packages/vue/src/drawer/src/pc.vue
+++ b/packages/vue/src/drawer/src/pc.vue
@@ -161,7 +161,8 @@ export default defineComponent({
'zIndex',
'beforeClose',
'tipsProps',
- 'customSlots'
+ 'customSlots',
+ 'closeOnPressEscape'
],
emits: ['update:visible', 'open', 'close', 'confirm', 'drag'],
setup(props, context) {