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

265 lines
6.9 KiB
Vue

<template>
<div :class="[ns.b(), ns.is('disabled', disabled)]">
<el-tooltip
ref="popperRef"
:role="role"
:effect="effect"
:fallback-placements="['bottom', 'top']"
:popper-options="popperOptions"
:gpu-acceleration="false"
:hide-after="trigger === 'hover' ? hideTimeout : 0"
:manual-mode="true"
:placement="placement"
:popper-class="[ns.e('popper'), popperClass]"
:reference-element="referenceElementRef?.$el"
:trigger="trigger"
:trigger-keys="triggerKeys"
:trigger-target-el="contentRef"
:show-after="trigger === 'hover' ? showTimeout : 0"
:stop-popper-mouse-event="false"
:virtual-ref="triggeringElementRef"
:virtual-triggering="splitButton"
:disabled="disabled"
:transition="`${ns.namespace.value}-zoom-in-top`"
teleported
pure
persistent
@before-show="handleBeforeShowTooltip"
@show="handleShowTooltip"
@before-hide="handleBeforeHideTooltip"
>
<template #content>
<el-scrollbar
ref="scrollbar"
:wrap-style="wrapStyle"
tag="div"
:view-class="ns.e('list')"
>
<el-roving-focus-group
:loop="loop"
:current-tab-id="currentTabId"
orientation="horizontal"
@current-tab-id-change="handleCurrentTabIdChange"
@entry-focus="handleEntryFocus"
>
<el-dropdown-collection>
<slot name="dropdown" />
</el-dropdown-collection>
</el-roving-focus-group>
</el-scrollbar>
</template>
<template v-if="!splitButton" #default>
<el-only-child :id="triggerId" role="button" :tabindex="tabindex">
<slot name="default" />
</el-only-child>
</template>
</el-tooltip>
<template v-if="splitButton">
<el-button-group>
<el-button
ref="referenceElementRef"
v-bind="buttonProps"
:size="dropdownSize"
:type="type"
:disabled="disabled"
:tabindex="tabindex"
@click="handlerMainButtonClick"
>
<slot name="default" />
</el-button>
<el-button
:id="triggerId"
ref="triggeringElementRef"
v-bind="buttonProps"
role="button"
:size="dropdownSize"
:type="type"
:class="ns.e('caret-button')"
:disabled="disabled"
:tabindex="tabindex"
:aria-label="t('el.dropdown.toggleDropdown')"
>
<el-icon :class="ns.e('icon')"><arrow-down /></el-icon>
</el-button>
</el-button-group>
</template>
</div>
</template>
<script lang="ts">
// @ts-nocheck
import {
computed,
defineComponent,
getCurrentInstance,
provide,
ref,
toRef,
unref,
} from 'vue'
import ElButton from '@element-plus/components/button'
import ElTooltip from '@element-plus/components/tooltip'
import ElScrollbar from '@element-plus/components/scrollbar'
import ElIcon from '@element-plus/components/icon'
import ElRovingFocusGroup from '@element-plus/components/roving-focus-group'
import { ElOnlyChild } from '@element-plus/components/slot'
import { addUnit } from '@element-plus/utils'
import { ArrowDown } from '@element-plus/icons-vue'
import { EVENT_CODE } from '@element-plus/constants'
import { useId, useLocale, useNamespace, useSize } from '@element-plus/hooks'
import { ElCollection as ElDropdownCollection, dropdownProps } from './dropdown'
import { DROPDOWN_INJECTION_KEY } from './tokens'
import type { CSSProperties } from 'vue'
const { ButtonGroup: ElButtonGroup } = ElButton
export default defineComponent({
name: 'ElDropdown',
components: {
ElButton,
ElButtonGroup,
ElScrollbar,
ElDropdownCollection,
ElTooltip,
ElRovingFocusGroup,
ElOnlyChild,
ElIcon,
ArrowDown,
},
props: dropdownProps,
emits: ['visible-change', 'click', 'command'],
setup(props, { emit }) {
const _instance = getCurrentInstance()
const ns = useNamespace('dropdown')
const { t } = useLocale()
const triggeringElementRef = ref()
const referenceElementRef = ref()
const popperRef = ref<InstanceType<typeof ElTooltip> | null>(null)
const contentRef = ref<HTMLElement | null>(null)
const scrollbar = ref(null)
const currentTabId = ref<string | null>(null)
const isUsingKeyboard = ref(false)
const triggerKeys = [EVENT_CODE.enter, EVENT_CODE.space, EVENT_CODE.down]
const wrapStyle = computed<CSSProperties>(() => ({
maxHeight: addUnit(props.maxHeight),
}))
const dropdownTriggerKls = computed(() => [ns.m(dropdownSize.value)])
const defaultTriggerId = useId().value
const triggerId = computed<string>(() => {
return props.id || defaultTriggerId
})
function handleClick() {
handleClose()
}
function handleClose() {
popperRef.value?.onClose()
}
function handleOpen() {
popperRef.value?.onOpen()
}
const dropdownSize = useSize()
function commandHandler(...args: any[]) {
emit('command', ...args)
}
function onItemEnter() {
// NOOP for now
}
function onItemLeave() {
const contentEl = unref(contentRef)
contentEl?.focus()
currentTabId.value = null
}
function handleCurrentTabIdChange(id: string) {
currentTabId.value = id
}
function handleEntryFocus(e: Event) {
if (!isUsingKeyboard.value) {
e.preventDefault()
e.stopImmediatePropagation()
}
}
function handleBeforeShowTooltip() {
emit('visible-change', true)
}
function handleShowTooltip(event?: Event) {
if (event?.type === 'keydown') {
contentRef.value.focus()
}
}
function handleBeforeHideTooltip() {
emit('visible-change', false)
}
provide(DROPDOWN_INJECTION_KEY, {
contentRef,
role: computed(() => props.role),
triggerId,
isUsingKeyboard,
onItemEnter,
onItemLeave,
})
provide('elDropdown', {
instance: _instance,
dropdownSize,
handleClick,
commandHandler,
trigger: toRef(props, 'trigger'),
hideOnClick: toRef(props, 'hideOnClick'),
})
const onFocusAfterTrapped = (e: Event) => {
e.preventDefault()
contentRef.value?.focus?.({
preventScroll: true,
})
}
const handlerMainButtonClick = (event: MouseEvent) => {
emit('click', event)
}
return {
t,
ns,
scrollbar,
wrapStyle,
dropdownTriggerKls,
dropdownSize,
triggerId,
triggerKeys,
currentTabId,
handleCurrentTabIdChange,
handlerMainButtonClick,
handleEntryFocus,
handleClose,
handleOpen,
handleBeforeShowTooltip,
handleShowTooltip,
handleBeforeHideTooltip,
onFocusAfterTrapped,
popperRef,
contentRef,
triggeringElementRef,
referenceElementRef,
}
},
})
</script>