fix: Refactor dropdown (#454)

This commit is contained in:
hangzou 2020-11-04 11:24:36 +08:00 committed by GitHub
parent 2932ba0e12
commit 8394a64478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 75 deletions

View File

@ -6,7 +6,7 @@ import css from 'rollup-plugin-css-only'
import { terser } from 'rollup-plugin-terser'
import typescript from 'rollup-plugin-typescript2'
import pkg from '../package.json'
const deps = Object.keys(pkg.dependencies)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const vue = require('./plugin.js')
@ -43,7 +43,7 @@ export default [
],
external(id) {
return /^vue/.test(id)
|| Object.keys(pkg.dependencies).some(k => new RegExp('^' + k).test(id))
|| deps.some(k => new RegExp('^' + k).test(id))
},
},
]

View File

@ -10,7 +10,7 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'
import path from 'path'
import { getPackagesSync } from '@lerna/project'
import pkg from '../package.json'
const deps = Object.keys(pkg.dependencies)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const vue = require('./plugin.js')
const inputs = getPackagesSync()
@ -64,6 +64,6 @@ export default inputs.map(name => ({
external(id) {
return /^vue/.test(id)
|| /^@element-plus/.test(id)
|| Object.keys(pkg.dependencies).some(k => new RegExp('^' + k).test(id))
|| deps.some(k => new RegExp('^' + k).test(id))
},
}))

View File

@ -50,9 +50,11 @@ describe('Dropdown', () => {
)
}
})
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
const content = wrapper.findComponent({ ref: 'b' }).vm as any
const triggerElm = wrapper.find('.el-dropdown-link')
expect(content.visible).toBe(false)
await triggerElm.trigger('keydown')
await triggerElm.trigger('focus')
await triggerElm.trigger(MOUSE_ENTER_EVENT)
await sleep(TIMEOUT)
expect(content.visible).toBe(true)
@ -91,7 +93,7 @@ describe('Dropdown', () => {
},
},
)
// const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
// const content = wrapper.findComponent({ ref: 'b' }).vm as any
const triggerElm = wrapper.find('.el-dropdown-link')
await triggerElm.trigger(MOUSE_ENTER_EVENT)
await sleep(TIMEOUT)
@ -123,7 +125,7 @@ describe('Dropdown', () => {
name: '',
}),
)
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
const content = wrapper.findComponent({ ref: 'b' }).vm as any
const triggerElm = wrapper.find('.el-dropdown-link')
expect(content.visible).toBe(false)
await triggerElm.trigger(MOUSE_ENTER_EVENT)
@ -162,12 +164,14 @@ describe('Dropdown', () => {
},
},
)
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
const content = wrapper.findComponent({ ref: 'b' }).vm as any
const triggerElm = wrapper.find('.el-dropdown__caret-button')
const button = wrapper.find('.el-button')
expect(content.visible).toBe(false)
await button.trigger('click')
expect((wrapper.vm as any).name).toBe('click')
await triggerElm.trigger('keydown')
await triggerElm.trigger('focus')
await triggerElm.trigger(MOUSE_ENTER_EVENT)
await sleep(TIMEOUT)
expect(content.visible).toBe(true)
@ -194,8 +198,10 @@ describe('Dropdown', () => {
() => ({}),
)
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
const content = wrapper.findComponent({ ref: 'b' }).vm as any
const triggerElm = wrapper.find('.el-dropdown-link')
await triggerElm.trigger('keydown')
await triggerElm.trigger('focus')
await triggerElm.trigger(MOUSE_ENTER_EVENT)
await sleep(TIMEOUT)
await wrapper.findComponent({ ref: 'c' }).trigger('click')
@ -224,8 +230,10 @@ describe('Dropdown', () => {
() => ({}),
)
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
const content = wrapper.findComponent({ ref: 'b' }).vm as any
const triggerElm = wrapper.find('.el-dropdown-link')
await triggerElm.trigger('keydown')
await triggerElm.trigger('focus')
await triggerElm.trigger(MOUSE_ENTER_EVENT)
await sleep(TIMEOUT)
await triggerElm.trigger('keydown', {

View File

@ -1,5 +1,6 @@
<template>
<ul
v-clickOutside:[triggerElm]="innerHide"
:class="[size && `el-dropdown-menu--${size}`]"
class="el-dropdown-menu"
@mouseenter.stop="show"
@ -10,10 +11,14 @@
</template>
<script lang='ts'>
import { defineComponent, getCurrentInstance, onMounted } from 'vue'
import { ClickOutside } from '@element-plus/directives'
import { useDropdown, initDropdownDomEvent } from './useDropdown'
export default defineComponent({
name: 'ElDropdownMenu',
directives: {
ClickOutside,
},
setup() {
const { _elDropdownSize, elDropdown } = useDropdown()
const size = _elDropdownSize.value
@ -38,6 +43,8 @@ export default defineComponent({
size,
show,
hide,
innerHide: _hide,
triggerElm: elDropdown.triggerElm,
}
},
})

View File

@ -1,20 +1,57 @@
<template>
<el-popper
ref="triggerVnode"
v-model:visible="visible"
:placement="placement"
:effect="effect"
:manual-mode="true"
:trigger="[trigger]"
popper-class="el-dropdown-popper"
>
<template #default>
<slot name="dropdown"></slot>
</template>
<template #trigger>
<div class="el-dropdown">
<slot v-if="!splitButton" name="default">
</slot>
<template v-else>
<el-button-group>
<el-button
:size="dropdownSize"
:type="type"
@click="handlerMainButtonClick"
>
<slot name="default"></slot>
</el-button>
<el-button
:size="dropdownSize"
:type="type"
class="el-dropdown__caret-button"
>
<i class="el-dropdown__icon el-icon-arrow-down"></i>
</el-button>
</el-button-group>
</template>
</div>
</template>
</el-popper>
</template>
<script lang='ts'>
import {
defineComponent,
h,
provide,
getCurrentInstance,
ref,
computed,
watch,
onMounted,
VNode,
nextTick,
ComponentPublicInstance,
watchEffect,
} from 'vue'
import { on, addClass, removeClass } from '@element-plus/utils/dom'
import { Button as ElButton, ButtonGroup as ElButtonGroup } from '@element-plus/button'
import { Popper as ELPopper } from '@element-plus/popper'
import { Popper as ElPopper } from '@element-plus/popper'
import { useDropdown } from './useDropdown'
export default defineComponent({
@ -22,7 +59,7 @@ export default defineComponent({
components: {
ElButton,
ElButtonGroup,
ELPopper,
ElPopper,
},
props: {
trigger: {
@ -61,7 +98,7 @@ export default defineComponent({
},
},
emits: ['visible-change', 'click', 'command'],
setup(props, { emit, slots }) {
setup(props, { emit }) {
const _instance = getCurrentInstance()
const { ELEMENT } = useDropdown()
@ -92,13 +129,13 @@ export default defineComponent({
},
)
const triggerVnode = ref<Nullable<VNode>>(null)
const caretButton = ref<Nullable<ComponentPublicInstance>>(null)
const triggerElm = computed<Nullable<HTMLButtonElement>>(() =>
!props.splitButton
? triggerVnode.value?.el
: caretButton.value?.$el,
)
const triggerVnode = ref<Nullable<ComponentPublicInstance>>(null)
const triggerElm = computed<Nullable<HTMLButtonElement>>(() =>{
const _: any = (triggerVnode.value?.$refs.triggerRef as HTMLElement)?.children[0] ?? {}
return !props.splitButton
? _
: _.children?.[1]
})
function handleClick() {
if (triggerElm.value?.disabled) return
@ -145,13 +182,6 @@ export default defineComponent({
triggerElm.value?.blur?.()
}
// for dom
Object.assign(_instance, {
handleClick,
hide,
resetTabindex,
})
const dropdownSize = computed(() => props.size || ELEMENT.size)
function commandHandler (...args) {
emit('command', ...args)
@ -188,6 +218,12 @@ export default defineComponent({
} else if (props.trigger === 'click') {
on(triggerElm.value, 'click', handleClick)
}
Object.assign(_instance, {
handleClick,
hide,
resetTabindex,
})
})
const handlerMainButtonClick = event => {
@ -195,49 +231,12 @@ export default defineComponent({
hide()
}
const onVisibleUpdate = (val: boolean) => visible.value = val
watchEffect(() => {
triggerVnode.value = !props.splitButton
? slots.default?.()[0]
: h(ElButtonGroup, {}, {
default: () => (
[
h(ElButton, {
type: props.type,
size: dropdownSize.value,
onClick: handlerMainButtonClick,
}, {
default: () => slots.default?.()[0],
}),
h(ElButton, {
type: props.type,
size: dropdownSize.value,
ref: caretButton,
class: 'el-dropdown__caret-button',
}, {
default: () => h('i', { class: 'el-dropdown__icon el-icon-arrow-down' }),
}),
]
),
})
})
return () => h(ELPopper, {
ref: 'popper',
placement: props.placement,
effect: props.effect,
visible: visible.value,
manualMode: true,
'onUpdate:visible': onVisibleUpdate,
popperClass: 'el-dropdown-popper',
trigger: [props.trigger],
}, {
default: () => slots.dropdown?.(),
trigger: () => h('div', {
class: 'el-dropdown',
}, [triggerVnode.value]),
})
return {
visible,
dropdownSize,
handlerMainButtonClick,
triggerVnode,
}
},
})
</script>

View File

@ -85,7 +85,7 @@ export const initDropdownDomEvent = (dropdownChildren, triggerElm, _instance) =>
triggerElm.setAttribute('aria-controls', listId.value)
if (!_instance.props.splitButton) {
triggerElm.setAttribute('role', 'button')
triggerElm.setAttribute('tabindex', _instance.tabindex)
triggerElm.setAttribute('tabindex', _instance.props.tabindex)
addClass(triggerElm, 'el-dropdown-selfdefine')
}
}
@ -104,7 +104,7 @@ export const initDropdownDomEvent = (dropdownChildren, triggerElm, _instance) =>
}
function triggerElmFocus() {
triggerElm?.focus?.()
triggerElm.focus()
}
initDomOperation()