feat(components): [drawer] add resize-start resize resize-end emits (#22784)

* feat(components): [drawer] add    emits

* chore: update

* chore: update
This commit is contained in:
Jevin
2025-11-13 14:32:00 +08:00
committed by GitHub
parent 5cae7273af
commit 43c1a9de82
5 changed files with 132 additions and 15 deletions

View File

@@ -137,14 +137,17 @@ Drawer provides an API called `destroy-on-close`, which is a flag variable that
### Events
| Name | Description | Type |
| ---------------- | ------------------------------------------------ | ----------------------- |
| open | Triggered before Drawer opening animation begins | ^[Function]`() => void` |
| opened | Triggered after Drawer opening animation ended | ^[Function]`() => void` |
| close | Triggered before Drawer closing animation begins | ^[Function]`() => void` |
| closed | Triggered after Drawer closing animation ended | ^[Function]`() => void` |
| open-auto-focus | triggers after Drawer opens and content focused | ^[Function]`() => void` |
| close-auto-focus | triggers after Drawer closed and content focused | ^[Function]`() => void` |
| Name | Description | Type |
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------ |
| open | Triggered before Drawer opening animation begins | ^[Function]`() => void` |
| opened | Triggered after Drawer opening animation ended | ^[Function]`() => void` |
| close | Triggered before Drawer closing animation begins | ^[Function]`() => void` |
| closed | Triggered after Drawer closing animation ended | ^[Function]`() => void` |
| open-auto-focus | triggers after Drawer opens and content focused | ^[Function]`() => void` |
| close-auto-focus | triggers after Drawer closed and content focused | ^[Function]`() => void` |
| resize-start ^(2.11.8) | Triggered when resizing starts (when `resizable` is enabled) | ^[Function]`(event: MouseEvent, size: number) => void` |
| resize ^(2.11.8) | Triggered while resizing (when `resizable` is enabled) | ^[Function]`(event: MouseEvent, size: number) => void` |
| resize-end ^(2.11.8) | Triggered when resizing ends (when `resizable` is enabled) | ^[Function]`(event: MouseEvent, size: number) => void` |
### Slots

View File

@@ -581,6 +581,100 @@ describe('Drawer', () => {
cleanup()
})
test('should emit resize-start, resize and resize-end events', async () => {
const cleanup = defineGetter(window, 'innerWidth', '100')
const onResizeStart = vi.fn()
const onResize = vi.fn()
const onResizeEnd = vi.fn()
const wrapper = _mount(
`
<el-drawer
v-model='visible'
direction='ltr'
resizable
size='50%'
@resize-start="onResizeStart"
@resize="onResize"
@resize-end="onResizeEnd">
<span>${content}</span>
</el-drawer>
`,
() => ({
visible: true,
}),
{
methods: {
onResizeStart,
onResize,
onResizeEnd,
},
}
)
await nextTick()
const dragger = wrapper.find('.el-drawer__dragger')
const drawerEl = wrapper.find('.el-drawer').element as HTMLDivElement
Object.defineProperty(drawerEl, 'offsetWidth', {
value: 50,
configurable: true,
})
const prop = 'pageX'
// Simulate mouse down - should trigger resize-start
const mousedown = new MouseEvent('mousedown', { bubbles: true })
const mousedownCleanup = defineGetter(mousedown, prop, 50)
dragger.element.dispatchEvent(mousedown)
await nextTick()
expect(onResizeStart).toHaveBeenCalledTimes(1)
expect(onResizeStart).toHaveBeenCalledWith(
expect.any(MouseEvent),
expect.any(Number)
)
// Simulate mouse move - should trigger resize
const mousemove = new MouseEvent('mousemove', { bubbles: true })
const mousemoveCleanup = defineGetter(mousemove, prop, 80)
window.dispatchEvent(mousemove)
await nextTick()
expect(onResize).toHaveBeenCalled()
expect(onResize).toHaveBeenCalledWith(
expect.any(MouseEvent),
expect.any(Number)
)
const resizeCallCount = onResize.mock.calls.length
// Simulate another mouse move
const mousemove2 = new MouseEvent('mousemove', { bubbles: true })
const mousemove2Cleanup = defineGetter(mousemove2, prop, 100)
window.dispatchEvent(mousemove2)
await nextTick()
expect(onResize).toHaveBeenCalledTimes(resizeCallCount + 1)
// Simulate mouse up - should trigger resize-end
const mouseup = new MouseEvent('mouseup', { bubbles: true })
const mouseupCleanup = defineGetter(mouseup, prop, 100)
window.dispatchEvent(mouseup)
await nextTick()
expect(onResizeEnd).toHaveBeenCalledTimes(1)
expect(onResizeEnd).toHaveBeenCalledWith(
expect.any(MouseEvent),
expect.any(Number)
)
mousedownCleanup()
mousemoveCleanup()
mousemove2Cleanup()
mouseupCleanup()
cleanup()
})
})
describe('accessibility', () => {

View File

@@ -2,12 +2,13 @@ import { computed, onBeforeUnmount, ref, watch } from 'vue'
import { addUnit } from '@element-plus/utils'
import { clamp, useEventListener, useWindowSize } from '@vueuse/core'
import type { Ref } from 'vue'
import type { DrawerProps } from '../drawer'
import type { Ref, SetupContext } from 'vue'
import type { DrawerEmits, DrawerProps } from '../drawer'
export function useResizable(
props: DrawerProps,
target: Ref<HTMLElement | undefined>
target: Ref<HTMLElement | undefined>,
emit: SetupContext<DrawerEmits>['emit']
) {
const { width, height } = useWindowSize()
@@ -61,6 +62,7 @@ export function useResizable(
startPos = [e.pageX, e.pageY]
isResizing.value = true
emit('resize-start', e, startSize.value)
cleanups.push(
useEventListener(window, 'mouseup', onMouseUp),
useEventListener(window, 'mousemove', onMouseMove)
@@ -72,15 +74,23 @@ export function useResizable(
const offsetX = pageX - startPos[0]
const offsetY = pageY - startPos[1]
offset.value = isHorizontal.value ? offsetX : offsetY
emit('resize', e, getSize.value)
}
const onMouseUp = () => {
const onMouseUp = (e?: MouseEvent) => {
// premature interruption
// avoid triggering meaningless execution due to watch size/resizable constraints.
if (!isResizing.value) return
startPos = []
startSize.value = getSize.value
offset.value = 0
isResizing.value = false
cleanups.forEach((cleanup) => cleanup?.())
cleanups = []
if (e) {
emit('resize-end', e, startSize.value)
}
}
const cleanup = useEventListener(target, 'mousedown', onMousedown)

View File

@@ -32,4 +32,14 @@ export const drawerProps = buildProps({
export type DrawerProps = ExtractPropTypes<typeof drawerProps>
export type DrawerPropsPublic = __ExtractPublicPropTypes<typeof drawerProps>
export const drawerEmits = dialogEmits
export const drawerEmits = {
...dialogEmits,
'resize-start': (event: MouseEvent, size: number) =>
event instanceof MouseEvent && typeof size === 'number',
resize: (event: MouseEvent, size: number) =>
event instanceof MouseEvent && typeof size === 'number',
'resize-end': (event: MouseEvent, size: number) =>
event instanceof MouseEvent && typeof size === 'number',
}
export type DrawerEmits = typeof drawerEmits

View File

@@ -123,7 +123,7 @@ defineOptions({
})
const props = defineProps(drawerProps)
defineEmits(drawerEmits)
const emit = defineEmits(drawerEmits)
const slots = useSlots()
useDeprecated(
@@ -160,7 +160,7 @@ const {
handleClose,
} = useDialog(props, drawerRef)
const { isHorizontal, size, isResizing } = useResizable(props, draggerRef)
const { isHorizontal, size, isResizing } = useResizable(props, draggerRef, emit)
const penetrable = computed(() => props.modalPenetrable && !props.modal)