element-plus/packages/components/dropdown/src/dropdown.vue

282 lines
6.6 KiB
Vue
Raw Normal View History

2020-11-04 11:24:36 +08:00
<template>
<el-popper
ref="triggerVnode"
v-model:visible="visible"
:placement="placement"
:fallback-placements="['bottom', 'top', 'right', 'left']"
2020-11-04 11:24:36 +08:00
:effect="effect"
pure
2020-11-04 11:24:36 +08:00
:manual-mode="true"
:trigger="[trigger]"
popper-class="el-dropdown__popper"
append-to-body
transition="el-zoom-in-top"
:stop-popper-mouse-event="false"
:gpu-acceleration="false"
2020-11-04 11:24:36 +08:00
>
<template #default>
<el-scrollbar
ref="scrollbar"
tag="ul"
:wrap-style="wrapStyle"
view-class="el-dropdown__list"
>
<slot name="dropdown"></slot>
</el-scrollbar>
2020-11-04 11:24:36 +08:00
</template>
<template #trigger>
<div :class="['el-dropdown', dropdownSize ? 'el-dropdown--' + dropdownSize : '']">
<slot v-if="!splitButton" name="default"></slot>
2020-11-04 11:24:36 +08:00
<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>
2020-11-12 14:43:55 +08:00
<script lang="ts">
2020-08-28 11:44:04 +08:00
import {
defineComponent,
provide,
getCurrentInstance,
ref,
computed,
watch,
onMounted,
} from 'vue'
import ElButton from '@element-plus/components/button'
import ElPopper from '@element-plus/components/popper'
import ElScrollbar from '@element-plus/components/scrollbar'
2020-08-28 11:44:04 +08:00
import { on, addClass, removeClass } from '@element-plus/utils/dom'
import { addUnit } from '@element-plus/utils/util'
import { useDropdown } from './useDropdown'
import type { ComponentPublicInstance } from 'vue'
2020-08-28 11:44:04 +08:00
type Nullable<T> = null | T
const { ButtonGroup: ElButtonGroup } = ElButton
2020-08-28 11:44:04 +08:00
export default defineComponent({
name: 'ElDropdown',
components: {
ElButton,
ElButtonGroup,
ElScrollbar,
2020-11-04 11:24:36 +08:00
ElPopper,
2020-08-28 11:44:04 +08:00
},
props: {
trigger: {
type: String,
default: 'hover',
},
type: String,
size: {
type: String,
default: '',
},
splitButton: Boolean,
hideOnClick: {
type: Boolean,
default: true,
},
placement: {
type: String,
default: 'bottom',
},
showTimeout: {
type: Number,
2020-09-21 15:39:53 +08:00
default: 150,
2020-08-28 11:44:04 +08:00
},
hideTimeout: {
type: Number,
default: 150,
},
tabindex: {
type: [Number, String],
2020-08-28 11:44:04 +08:00
default: 0,
},
effect: {
type: String,
default: 'light',
},
maxHeight: {
type: [Number, String],
default: '',
},
2020-08-28 11:44:04 +08:00
},
emits: ['visible-change', 'click', 'command'],
2020-11-04 11:24:36 +08:00
setup(props, { emit }) {
2020-08-28 11:44:04 +08:00
const _instance = getCurrentInstance()
const { ELEMENT } = useDropdown()
const timeout = ref<Nullable<number>>(null)
const visible = ref(false)
const scrollbar = ref(null)
const wrapStyle = computed(() => `max-height: ${addUnit(props.maxHeight)}`)
2020-08-28 11:44:04 +08:00
watch(
() => visible.value,
val => {
2020-11-12 14:43:55 +08:00
if (val) triggerElmFocus()
if (!val) triggerElmBlur()
2020-08-28 11:44:04 +08:00
emit('visible-change', val)
},
)
const focusing = ref(false)
watch(
() => focusing.value,
val => {
const selfDefine = triggerElm.value
if (selfDefine) {
if (val) {
addClass(selfDefine, 'focusing')
} else {
removeClass(selfDefine, 'focusing')
}
}
},
)
2020-11-04 11:24:36 +08:00
const triggerVnode = ref<Nullable<ComponentPublicInstance>>(null)
2020-11-12 14:43:55 +08:00
const triggerElm = computed<Nullable<HTMLButtonElement>>(() => {
const _: any =
(triggerVnode.value?.$refs.triggerRef as HTMLElement)?.children[0] ?? {}
return !props.splitButton ? _ : _.children?.[1]
2020-11-04 11:24:36 +08:00
})
2020-08-28 11:44:04 +08:00
function handleClick() {
if (triggerElm.value?.disabled) return
if (visible.value) {
hide()
} else {
show()
}
}
function show() {
if (triggerElm.value?.disabled) return
timeout.value && clearTimeout(timeout.value)
2020-11-12 14:43:55 +08:00
timeout.value = window.setTimeout(
() => {
visible.value = true
},
['click', 'contextmenu'].includes(props.trigger) ? 0 : props.showTimeout,
2020-11-12 14:43:55 +08:00
)
2020-08-28 11:44:04 +08:00
}
function hide() {
if (triggerElm.value?.disabled) return
removeTabindex()
2020-11-12 14:43:55 +08:00
if (props.tabindex >= 0) {
2020-08-28 11:44:04 +08:00
resetTabindex(triggerElm.value)
}
clearTimeout(timeout.value)
2020-11-12 14:43:55 +08:00
timeout.value = window.setTimeout(
() => {
visible.value = false
},
['click', 'contextmenu'].includes(props.trigger) ? 0 : props.hideTimeout,
2020-11-12 14:43:55 +08:00
)
2020-08-28 11:44:04 +08:00
}
function removeTabindex() {
triggerElm.value?.setAttribute('tabindex', '-1')
}
function resetTabindex(ele) {
removeTabindex()
ele?.setAttribute('tabindex', '0')
}
function triggerElmFocus() {
triggerElm.value?.focus?.()
}
2020-08-28 11:44:04 +08:00
function triggerElmBlur() {
triggerElm.value?.blur?.()
}
const dropdownSize = computed(() => props.size || ELEMENT.size)
2020-11-12 14:43:55 +08:00
function commandHandler(...args) {
2020-08-28 11:44:04 +08:00
emit('command', ...args)
}
provide('elDropdown', {
instance: _instance,
dropdownSize,
visible,
handleClick,
commandHandler,
show,
hide,
trigger: computed(() => props.trigger),
hideOnClick: computed(() => props.hideOnClick),
triggerElm,
})
onMounted(() => {
if (!props.splitButton) {
on(triggerElm.value, 'focus', () => {
focusing.value = true
})
on(triggerElm.value, 'blur', () => {
focusing.value = false
})
on(triggerElm.value, 'click', () => {
focusing.value = false
})
}
if (props.trigger === 'hover') {
on(triggerElm.value, 'mouseenter', show)
on(triggerElm.value, 'mouseleave', hide)
} else if (props.trigger === 'click') {
on(triggerElm.value, 'click', handleClick)
} else if (props.trigger === 'contextmenu') {
on(triggerElm.value, 'contextmenu', e => {
e.preventDefault()
handleClick()
})
2020-08-28 11:44:04 +08:00
}
2020-11-04 11:24:36 +08:00
Object.assign(_instance, {
handleClick,
hide,
resetTabindex,
})
2020-08-28 11:44:04 +08:00
})
const handlerMainButtonClick = event => {
emit('click', event)
hide()
}
2020-11-04 11:24:36 +08:00
return {
visible,
scrollbar,
wrapStyle,
2020-11-04 11:24:36 +08:00
dropdownSize,
handlerMainButtonClick,
triggerVnode,
}
2020-08-28 11:44:04 +08:00
},
})
</script>