2020-11-04 11:24:36 +08:00
|
|
|
<template>
|
2021-11-05 15:33:17 +08:00
|
|
|
<div class="el-dropdown">
|
|
|
|
<el-popper
|
|
|
|
ref="triggerVnode"
|
|
|
|
v-model:visible="visible"
|
|
|
|
:placement="placement"
|
|
|
|
:fallback-placements="['bottom', 'top', 'right', 'left']"
|
|
|
|
:effect="effect"
|
|
|
|
pure
|
|
|
|
: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"
|
|
|
|
>
|
|
|
|
<template #default>
|
|
|
|
<el-scrollbar
|
|
|
|
ref="scrollbar"
|
|
|
|
tag="ul"
|
|
|
|
:wrap-style="wrapStyle"
|
|
|
|
view-class="el-dropdown__list"
|
|
|
|
>
|
|
|
|
<slot name="dropdown"></slot>
|
|
|
|
</el-scrollbar>
|
|
|
|
</template>
|
|
|
|
<template #trigger>
|
|
|
|
<div :class="[dropdownSize ? 'el-dropdown--' + dropdownSize : '']">
|
|
|
|
<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"
|
|
|
|
>
|
|
|
|
<el-icon class="el-dropdown__icon"><arrow-down /></el-icon>
|
|
|
|
</el-button>
|
|
|
|
</el-button-group>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-popper>
|
|
|
|
</div>
|
2020-11-04 11:24:36 +08:00
|
|
|
</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'
|
2021-08-24 13:36:48 +08:00
|
|
|
import ElButton from '@element-plus/components/button'
|
2021-09-17 15:27:31 +08:00
|
|
|
import ElPopper, { Effect } from '@element-plus/components/popper'
|
2021-08-24 13:36:48 +08:00
|
|
|
import ElScrollbar from '@element-plus/components/scrollbar'
|
2021-10-28 23:37:26 +08:00
|
|
|
import ElIcon from '@element-plus/components/icon'
|
2020-08-28 11:44:04 +08:00
|
|
|
import { on, addClass, removeClass } from '@element-plus/utils/dom'
|
2021-02-08 15:14:01 +08:00
|
|
|
import { addUnit } from '@element-plus/utils/util'
|
2021-12-04 11:20:06 +08:00
|
|
|
import { ArrowDown } from '@element-plus/icons-vue'
|
2021-12-10 17:21:01 +08:00
|
|
|
import { useSize } from '@element-plus/hooks'
|
2021-10-27 23:17:13 +08:00
|
|
|
|
2021-09-17 15:27:31 +08:00
|
|
|
import type { Placement } from '@element-plus/components/popper'
|
2021-11-12 06:46:13 +08:00
|
|
|
import type { PropType, ComponentPublicInstance, CSSProperties } from 'vue'
|
2021-08-31 09:40:13 +08:00
|
|
|
import type { TriggerType } from '@element-plus/hooks/use-popper/use-target-events'
|
|
|
|
import type { ButtonType } from '@element-plus/components/button/src/types'
|
2020-08-28 11:44:04 +08:00
|
|
|
|
2021-08-04 18:28:08 +08:00
|
|
|
type Nullable<T> = null | T
|
2021-08-24 13:36:48 +08:00
|
|
|
const { ButtonGroup: ElButtonGroup } = ElButton
|
2021-08-04 18:28:08 +08:00
|
|
|
|
2020-08-28 11:44:04 +08:00
|
|
|
export default defineComponent({
|
|
|
|
name: 'ElDropdown',
|
|
|
|
components: {
|
|
|
|
ElButton,
|
|
|
|
ElButtonGroup,
|
2021-02-08 15:14:01 +08:00
|
|
|
ElScrollbar,
|
2020-11-04 11:24:36 +08:00
|
|
|
ElPopper,
|
2021-10-27 23:17:13 +08:00
|
|
|
ElIcon,
|
|
|
|
ArrowDown,
|
2020-08-28 11:44:04 +08:00
|
|
|
},
|
|
|
|
props: {
|
|
|
|
trigger: {
|
2021-08-31 17:57:35 +08:00
|
|
|
type: String as PropType<TriggerType | 'contextmenu'>,
|
2020-08-28 11:44:04 +08:00
|
|
|
default: 'hover',
|
|
|
|
},
|
2021-08-31 09:40:13 +08:00
|
|
|
type: String as PropType<ButtonType>,
|
2020-08-28 11:44:04 +08:00
|
|
|
size: {
|
|
|
|
type: String,
|
|
|
|
default: '',
|
|
|
|
},
|
|
|
|
splitButton: Boolean,
|
|
|
|
hideOnClick: {
|
|
|
|
type: Boolean,
|
|
|
|
default: true,
|
|
|
|
},
|
|
|
|
placement: {
|
2021-08-31 09:40:13 +08:00
|
|
|
type: String as PropType<Placement>,
|
2020-08-28 11:44:04 +08:00
|
|
|
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: {
|
2021-06-03 12:55:51 +08:00
|
|
|
type: [Number, String],
|
2020-08-28 11:44:04 +08:00
|
|
|
default: 0,
|
|
|
|
},
|
|
|
|
effect: {
|
2021-08-31 09:40:13 +08:00
|
|
|
type: String as PropType<Effect>,
|
|
|
|
default: Effect.LIGHT,
|
2020-08-28 11:44:04 +08:00
|
|
|
},
|
2021-02-08 15:14:01 +08:00
|
|
|
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 timeout = ref<Nullable<number>>(null)
|
|
|
|
|
|
|
|
const visible = ref(false)
|
2021-02-08 15:14:01 +08:00
|
|
|
const scrollbar = ref(null)
|
2021-11-12 06:46:13 +08:00
|
|
|
const wrapStyle = computed<CSSProperties>(() => ({
|
|
|
|
maxHeight: addUnit(props.maxHeight),
|
|
|
|
}))
|
2021-02-08 15:14:01 +08:00
|
|
|
|
2020-08-28 11:44:04 +08:00
|
|
|
watch(
|
|
|
|
() => visible.value,
|
2021-09-04 19:29:28 +08:00
|
|
|
(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)
|
2021-09-04 19:29:28 +08:00
|
|
|
}
|
2020-08-28 11:44:04 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const focusing = ref(false)
|
|
|
|
watch(
|
|
|
|
() => focusing.value,
|
2021-09-04 19:29:28 +08:00
|
|
|
(val) => {
|
2020-08-28 11:44:04 +08:00
|
|
|
const selfDefine = triggerElm.value
|
|
|
|
if (selfDefine) {
|
|
|
|
if (val) {
|
|
|
|
addClass(selfDefine, 'focusing')
|
|
|
|
} else {
|
|
|
|
removeClass(selfDefine, 'focusing')
|
|
|
|
}
|
|
|
|
}
|
2021-09-04 19:29:28 +08:00
|
|
|
}
|
2020-08-28 11:44:04 +08:00
|
|
|
)
|
|
|
|
|
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>>(() => {
|
2021-09-04 19:29:28 +08:00
|
|
|
const _: any = (triggerVnode.value?.$refs.triggerRef as HTMLElement)
|
|
|
|
?.children[0]
|
2021-09-01 17:38:54 +08:00
|
|
|
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
|
|
|
|
},
|
2021-09-04 19:29:28 +08:00
|
|
|
['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
|
|
|
|
},
|
2021-09-04 19:29:28 +08:00
|
|
|
['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?.()
|
|
|
|
}
|
2021-02-08 15:14:01 +08:00
|
|
|
|
2020-08-28 11:44:04 +08:00
|
|
|
function triggerElmBlur() {
|
|
|
|
triggerElm.value?.blur?.()
|
|
|
|
}
|
|
|
|
|
2021-12-10 17:21:01 +08:00
|
|
|
const dropdownSize = useSize()
|
2021-02-08 15:14:01 +08:00
|
|
|
|
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)
|
2021-01-25 11:21:31 +08:00
|
|
|
} else if (props.trigger === 'contextmenu') {
|
2021-09-04 19:29:28 +08:00
|
|
|
on(triggerElm.value, 'contextmenu', (e) => {
|
2021-01-25 11:21:31 +08:00
|
|
|
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
|
|
|
})
|
|
|
|
|
2021-09-04 19:29:28 +08:00
|
|
|
const handlerMainButtonClick = (event) => {
|
2020-08-28 11:44:04 +08:00
|
|
|
emit('click', event)
|
|
|
|
hide()
|
|
|
|
}
|
|
|
|
|
2020-11-04 11:24:36 +08:00
|
|
|
return {
|
|
|
|
visible,
|
2021-02-08 15:14:01 +08:00
|
|
|
scrollbar,
|
|
|
|
wrapStyle,
|
2020-11-04 11:24:36 +08:00
|
|
|
dropdownSize,
|
|
|
|
handlerMainButtonClick,
|
|
|
|
triggerVnode,
|
|
|
|
}
|
2020-08-28 11:44:04 +08:00
|
|
|
},
|
|
|
|
})
|
|
|
|
</script>
|