mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-12-02 03:08:21 +08:00
Feat/dropdown (#199)
This commit is contained in:
parent
dec5d665a3
commit
7931f2a2fb
@ -3,7 +3,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"cz": "git add -A && npx git-cz",
|
"cz": "npx git-cz",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"gen": "bash ./scripts/gc.sh",
|
"gen": "bash ./scripts/gc.sh",
|
||||||
"storybook": "start-storybook",
|
"storybook": "start-storybook",
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
<el-checkbox v-model="checked2" disabled>{{ checked2 }}</el-checkbox>
|
<el-checkbox v-model="checked2" disabled>{{ checked2 }}</el-checkbox>
|
||||||
<el-checkbox v-model="checked1" label="A" border />
|
<el-checkbox v-model="checked1" label="A" border />
|
||||||
<el-checkbox v-model="checked2" border />
|
<el-checkbox v-model="checked2" border />
|
||||||
|
<el-checkbox v-model="checked4" true-label="a" :false-label="3" />
|
||||||
|
{{ checked4 }}
|
||||||
<el-checkbox-group v-model="checkList">
|
<el-checkbox-group v-model="checkList">
|
||||||
<el-checkbox label="A" />
|
<el-checkbox label="A" />
|
||||||
<el-checkbox label="B" />
|
<el-checkbox label="B" />
|
||||||
@ -25,6 +26,7 @@ export default defineComponent({
|
|||||||
checked1: false,
|
checked1: false,
|
||||||
checked2: true,
|
checked2: true,
|
||||||
checkList: ['Ha','A'],
|
checkList: ['Ha','A'],
|
||||||
|
checked4: 3,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -52,7 +52,7 @@ import {
|
|||||||
defineComponent,
|
defineComponent,
|
||||||
ref,
|
ref,
|
||||||
computed,
|
computed,
|
||||||
// nextTick,
|
PropType,
|
||||||
watch,
|
watch,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { useCheckbox } from './useCheckbox'
|
import { useCheckbox } from './useCheckbox'
|
||||||
@ -61,12 +61,11 @@ export default defineComponent({
|
|||||||
name: 'ElCheckboxButton',
|
name: 'ElCheckboxButton',
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [Object, Boolean],
|
type: [Object, Boolean, String, Number] as PropType<Record<string, unknown> | boolean | number>,
|
||||||
default: () => undefined,
|
default: () => undefined,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
type: [Object, Boolean, String],
|
type: [Object, Boolean, String] as PropType<Record<string, unknown> | boolean | string>,
|
||||||
default: () => ({}),
|
|
||||||
},
|
},
|
||||||
indeterminate: Boolean,
|
indeterminate: Boolean,
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
|
@ -64,7 +64,7 @@ import {
|
|||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
watch,
|
watch,
|
||||||
onMounted,
|
onMounted,
|
||||||
// nextTick,
|
PropType,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { useCheckbox } from './useCheckbox'
|
import { useCheckbox } from './useCheckbox'
|
||||||
|
|
||||||
@ -72,12 +72,11 @@ export default defineComponent({
|
|||||||
name: 'ElCheckbox',
|
name: 'ElCheckbox',
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [Object, Boolean],
|
type: [Object, Boolean, String, Number] as PropType<Record<string, unknown> | boolean | number>,
|
||||||
default: () => undefined,
|
default: () => undefined,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
type: [Object, Boolean, String],
|
type: [Object, Boolean, String] as PropType<Record<string, unknown> | boolean | string>,
|
||||||
default: ' ',
|
|
||||||
},
|
},
|
||||||
indeterminate: Boolean,
|
indeterminate: Boolean,
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
|
@ -9,7 +9,6 @@ export const useCheckbox = () => {
|
|||||||
const focus = ref(false)
|
const focus = ref(false)
|
||||||
const isGroup = computed(() => _checkboxGroup && _checkboxGroup.name === 'ElCheckboxGroup')
|
const isGroup = computed(() => _checkboxGroup && _checkboxGroup.name === 'ElCheckboxGroup')
|
||||||
const _elFormItemSize = computed(() => {
|
const _elFormItemSize = computed(() => {
|
||||||
|
|
||||||
return (elFormItem || {} as any).elFormItemSize
|
return (elFormItem || {} as any).elFormItemSize
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
273
packages/dropdown/__tests__/dropdown.spec.ts
Normal file
273
packages/dropdown/__tests__/dropdown.spec.ts
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import { eventKeys } from '@element-plus/utils/aria'
|
||||||
|
import Dropdown from '../src/dropdown.vue'
|
||||||
|
import DropdownItem from '../src/dropdown-item.vue'
|
||||||
|
import DropdownMenu from '../src/dropdown-menu.vue'
|
||||||
|
|
||||||
|
const MOUSE_ENTER_EVENT = 'mouseenter'
|
||||||
|
const MOUSE_LEAVE_EVENT = 'mouseleave'
|
||||||
|
const CLICK = 'click'
|
||||||
|
|
||||||
|
const _mount = (template: string, data, otherObj?) => mount({
|
||||||
|
components: {
|
||||||
|
[Dropdown.name]: Dropdown,
|
||||||
|
[DropdownItem.name]: DropdownItem,
|
||||||
|
[DropdownMenu.name]: DropdownMenu,
|
||||||
|
},
|
||||||
|
template,
|
||||||
|
data,
|
||||||
|
...otherObj,
|
||||||
|
})
|
||||||
|
const sleep = (time = 250) => new Promise(resolve => setTimeout(resolve, time))
|
||||||
|
export const timeout = async (fn, time = 250) => {
|
||||||
|
await sleep(time)
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Dropdown', () => {
|
||||||
|
test('create', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown ref="b" placement="right">
|
||||||
|
<span class="el-dropdown-link" ref="a">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({}),
|
||||||
|
)
|
||||||
|
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown-link')
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(true)
|
||||||
|
await triggerElm.trigger(MOUSE_LEAVE_EVENT)
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('menu click', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown ref="b" @command="commandHandler" placement="right">
|
||||||
|
<span class="el-dropdown-link" ref="a">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="a">Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="b">Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item ref="c" :command="myCommandObject">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="d">Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="e">Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({
|
||||||
|
myCommandObject: { name: 'CommandC' },
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
methods: {
|
||||||
|
commandHandler(command) {
|
||||||
|
this.name = command.name
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown-link')
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
await wrapper.findComponent({ ref: 'c' }).trigger('click')
|
||||||
|
await sleep()
|
||||||
|
expect((wrapper.vm as any).name).toBe('CommandC')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('trigger', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown trigger="click" ref="b" placement="right">
|
||||||
|
<span class="el-dropdown-link" ref="a">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="a">Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="b">Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item ref="c" :command="myCommandObject">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="d">Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="e">Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({
|
||||||
|
myCommandObject: { name: 'CommandC' },
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown-link')
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
await triggerElm.trigger(CLICK)
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('split button', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown @click="handleClick" split-button type="primary" ref="b" placement="right">
|
||||||
|
dropdown
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="a">Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="b">Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item ref="c" :command="myCommandObject">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="d">Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="e">Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({
|
||||||
|
myCommandObject: { name: 'CommandC' },
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
this.name = 'click'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown__caret-button')
|
||||||
|
const button = wrapper.find('.el-button')
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
await button.trigger('click')
|
||||||
|
expect((wrapper.vm as any).name).toBe('click')
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hide on click', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown ref="b" placement="right" :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link" ref="a">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item ref="c">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown-link')
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
await wrapper.findComponent({ ref: 'c' }).trigger('click')
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('triggerElm keydown', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown ref="b" placement="right" :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link" ref="a">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item ref="c">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const content = wrapper.findComponent({ ref: 'b' }).vm.$refs.popper as any
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown-link')
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
await triggerElm.trigger('keydown', {
|
||||||
|
keyCode: eventKeys.enter,
|
||||||
|
})
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
await triggerElm.trigger('keydown', {
|
||||||
|
keyCode: eventKeys.tab,
|
||||||
|
})
|
||||||
|
await sleep()
|
||||||
|
expect(content.value).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dropdown menu keydown', async () => {
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-dropdown ref="b" placement="right" :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link" ref="a">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu ref="a">
|
||||||
|
<el-dropdown-item ref="d">Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item ref="c">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
`,
|
||||||
|
() => ({}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const content = wrapper.findComponent({ ref: 'a' })
|
||||||
|
const triggerElm = wrapper.find('.el-dropdown-link')
|
||||||
|
await triggerElm.trigger(MOUSE_ENTER_EVENT)
|
||||||
|
await sleep()
|
||||||
|
await content.trigger('keydown', {
|
||||||
|
keyCode: eventKeys.down,
|
||||||
|
})
|
||||||
|
await sleep()
|
||||||
|
expect(wrapper.findComponent({ ref: 'd' }).attributes('tabindex')).toBe('0')
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
164
packages/dropdown/doc/basic.vue
Normal file
164
packages/dropdown/doc/basic.vue
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<el-dropdown ref="c" placement="right" :hide-on-click="false">
|
||||||
|
<span ref="a" class="el-dropdown-link">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu ref="b">
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown trigger="click" :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown>
|
||||||
|
<el-button type="primary">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown @command="handleCommand">
|
||||||
|
<el-button type="primary">
|
||||||
|
dropdown<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="a">Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="b">Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="c">Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="d" disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="e" divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown split-button type="primary" @click="handleClick">
|
||||||
|
dropdown
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown
|
||||||
|
split-button
|
||||||
|
size="medium"
|
||||||
|
type="primary"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
dropdown
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown
|
||||||
|
split-button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
dropdown
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<el-dropdown
|
||||||
|
split-button
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
dropdown
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Apple</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Orange</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Cherry</el-dropdown-item>
|
||||||
|
<el-dropdown-item disabled>Peach</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided>Pear</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
export default defineComponent({
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
alert('button click')
|
||||||
|
},
|
||||||
|
handleCommand(command) {
|
||||||
|
alert('click on item ' + command)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-dropdown-link {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
.el-icon-arrow-down {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.el-dropdown-menu__item {
|
||||||
|
word-break: keep-all;
|
||||||
|
}
|
||||||
|
</style>
|
6
packages/dropdown/doc/index.stories.ts
Normal file
6
packages/dropdown/doc/index.stories.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export { default as BasicUsage } from './basic.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Dropdown',
|
||||||
|
}
|
||||||
|
|
10
packages/dropdown/index.ts
Normal file
10
packages/dropdown/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { App } from 'vue'
|
||||||
|
import Dropdown from './src/dropdown.vue'
|
||||||
|
import DropdownItem from './src/dropdown-item.vue'
|
||||||
|
import DropdownMenu from './src/dropdown-menu.vue'
|
||||||
|
|
||||||
|
export default (app: App): void => {
|
||||||
|
app.component(Dropdown.name, Dropdown)
|
||||||
|
app.component(DropdownItem.name, DropdownItem)
|
||||||
|
app.component(DropdownMenu.name, DropdownMenu)
|
||||||
|
}
|
12
packages/dropdown/package.json
Normal file
12
packages/dropdown/package.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "@element-plus/dropdown",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0-rc.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/test-utils": "^2.0.0-beta.0"
|
||||||
|
}
|
||||||
|
}
|
49
packages/dropdown/src/dropdown-item.vue
Normal file
49
packages/dropdown/src/dropdown-item.vue
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<li
|
||||||
|
class="el-dropdown-menu__item"
|
||||||
|
:class="{
|
||||||
|
'is-disabled': disabled,
|
||||||
|
'el-dropdown-menu__item--divided': divided
|
||||||
|
}"
|
||||||
|
:aria-disabled="disabled"
|
||||||
|
:tabindex="disabled ? null : -1"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<i v-if="icon" :class="icon"></i>
|
||||||
|
<slot></slot>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
<script lang='ts'>
|
||||||
|
import { defineComponent, getCurrentInstance } from 'vue'
|
||||||
|
import { useDropdown } from './useDropdown'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ElDropdownItem',
|
||||||
|
props: {
|
||||||
|
command: {
|
||||||
|
type: [Object, String, Number],
|
||||||
|
defautl: () => ({}),
|
||||||
|
},
|
||||||
|
disabled: Boolean,
|
||||||
|
divided: Boolean,
|
||||||
|
icon: String,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const { elDropdown } = useDropdown()
|
||||||
|
const _instance = getCurrentInstance()
|
||||||
|
|
||||||
|
function handleClick(e: UIEvent) {
|
||||||
|
if (elDropdown.hideOnClick.value) {
|
||||||
|
elDropdown.handleClick?.()
|
||||||
|
}
|
||||||
|
elDropdown.commandHandler?.(props.command, _instance, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleClick,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
59
packages/dropdown/src/dropdown-menu.vue
Normal file
59
packages/dropdown/src/dropdown-menu.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<ul
|
||||||
|
v-clickOutside:[_trigger]="_hide"
|
||||||
|
:class="[size && `el-dropdown-menu--${size}`]"
|
||||||
|
class="el-dropdown-menu"
|
||||||
|
@mouseenter.stop="show"
|
||||||
|
@mouseleave.stop="hide"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
<script lang='ts'>
|
||||||
|
import { defineComponent, getCurrentInstance, onMounted, ref } from 'vue'
|
||||||
|
import ClickOutside from '@element-plus/directives/click-outside'
|
||||||
|
import { useDropdown, initDropdownDomEvent } from './useDropdown'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ElDropdownMenu',
|
||||||
|
directives: {
|
||||||
|
ClickOutside,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const { _elDropdownSize, elDropdown } = useDropdown()
|
||||||
|
const size = _elDropdownSize.value
|
||||||
|
const _trigger = ref(null)
|
||||||
|
function show() {
|
||||||
|
elDropdown.show?.()
|
||||||
|
}
|
||||||
|
function hide() {
|
||||||
|
if (elDropdown.trigger.value === 'click') return
|
||||||
|
_hide()
|
||||||
|
}
|
||||||
|
function _hide() {
|
||||||
|
elDropdown.hide?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const dropdownMenu = getCurrentInstance()
|
||||||
|
_trigger.value = elDropdown.triggerElm.value
|
||||||
|
initDropdownDomEvent(dropdownMenu, _trigger.value, elDropdown.instance)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
size,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
_trigger,
|
||||||
|
_hide,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.el-dropdown-menu {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
</style>
|
18
packages/dropdown/src/dropdown.d.ts
vendored
Normal file
18
packages/dropdown/src/dropdown.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
ComponentInternalInstance,
|
||||||
|
ComputedRef,
|
||||||
|
Ref,
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
export interface IElDropdownInstance {
|
||||||
|
instance?: ComponentInternalInstance
|
||||||
|
dropdownSize?: ComputedRef<string>
|
||||||
|
visible?: Ref<boolean>
|
||||||
|
handleClick?: () => void
|
||||||
|
commandHandler?: (...arg) => void
|
||||||
|
show?: () => void
|
||||||
|
hide?: () => void
|
||||||
|
trigger?: ComputedRef<string>
|
||||||
|
hideOnClick?: ComputedRef<boolean>
|
||||||
|
triggerElm?: ComputedRef<Nullable<HTMLButtonElement>>
|
||||||
|
}
|
261
packages/dropdown/src/dropdown.vue
Normal file
261
packages/dropdown/src/dropdown.vue
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
<script lang='ts'>
|
||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
h,
|
||||||
|
provide,
|
||||||
|
getCurrentInstance,
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
watch,
|
||||||
|
onMounted,
|
||||||
|
VNode,
|
||||||
|
} from 'vue'
|
||||||
|
import { on, addClass, removeClass } from '@element-plus/utils/dom'
|
||||||
|
import ClickOutside from '@element-plus/directives/click-outside'
|
||||||
|
import ElButton from '@element-plus/button/src/button.vue'
|
||||||
|
import ElButtonGroup from '@element-plus/button/src/button-group.vue'
|
||||||
|
import ELPopper from '@element-plus/popper/src/index.vue'
|
||||||
|
import { useDropdown } from './useDropdown'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ElDropdown',
|
||||||
|
directives: {
|
||||||
|
ClickOutside,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ElButton,
|
||||||
|
ElButtonGroup,
|
||||||
|
ELPopper,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
default: 250,
|
||||||
|
},
|
||||||
|
hideTimeout: {
|
||||||
|
type: Number,
|
||||||
|
default: 150,
|
||||||
|
},
|
||||||
|
tabindex: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
effect: {
|
||||||
|
type: String,
|
||||||
|
default: 'light',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['visible-change', 'click', 'command'],
|
||||||
|
setup(props, { emit, slots }) {
|
||||||
|
const _instance = getCurrentInstance()
|
||||||
|
const { ELEMENT } = useDropdown()
|
||||||
|
|
||||||
|
const timeout = ref<Nullable<number>>(null)
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
watch(
|
||||||
|
() => visible.value,
|
||||||
|
val => {
|
||||||
|
if(val) triggerElmFocus()
|
||||||
|
if(!val) triggerElmBlur()
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const triggerVnode = ref<Nullable<VNode>>(null)
|
||||||
|
const triggerElm = computed<Nullable<HTMLButtonElement>>(() =>
|
||||||
|
!props.splitButton
|
||||||
|
? triggerVnode.value?.el
|
||||||
|
: triggerVnode.value?.el.querySelector('.el-dropdown__caret-button'),
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
timeout.value = window.setTimeout(() => {
|
||||||
|
visible.value = true
|
||||||
|
}, props.trigger === 'click' ? 0 : props.showTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
if (triggerElm.value?.disabled) return
|
||||||
|
removeTabindex()
|
||||||
|
if (props.tabindex >=0) {
|
||||||
|
resetTabindex(triggerElm.value)
|
||||||
|
}
|
||||||
|
clearTimeout(timeout.value)
|
||||||
|
timeout.value = window.setTimeout(() => {
|
||||||
|
visible.value = false
|
||||||
|
}, props.trigger === 'click' ? 0 : props.hideTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTabindex() {
|
||||||
|
triggerElm.value?.setAttribute('tabindex', '-1')
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTabindex(ele) {
|
||||||
|
removeTabindex()
|
||||||
|
ele?.setAttribute('tabindex', '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerElmFocus() {
|
||||||
|
triggerElm.value?.focus?.()
|
||||||
|
}
|
||||||
|
function triggerElmBlur() {
|
||||||
|
triggerElm.value?.blur?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
// for dom
|
||||||
|
Object.assign(_instance, {
|
||||||
|
handleClick,
|
||||||
|
hide,
|
||||||
|
resetTabindex,
|
||||||
|
})
|
||||||
|
|
||||||
|
const dropdownSize = computed(() => props.size || (ELEMENT || {}).size)
|
||||||
|
function commandHandler (...args) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handlerMainButtonClick = event => {
|
||||||
|
emit('click', event)
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerVnode.value = !props.splitButton
|
||||||
|
? slots.default?.()[0] // trigger must be a single root element
|
||||||
|
: 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,
|
||||||
|
class: 'el-dropdown__caret-button',
|
||||||
|
}, {
|
||||||
|
default: () => h('i', { class: 'el-dropdown__icon el-icon-arrow-down' }),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
})
|
||||||
|
slots.default?.().length > 1 && console.warn('trigger must be a single root element')
|
||||||
|
|
||||||
|
const dropdownVnode = h('div', {
|
||||||
|
class: 'el-dropdown',
|
||||||
|
}, [triggerVnode.value])
|
||||||
|
|
||||||
|
return () => h(ELPopper, {
|
||||||
|
ref: 'popper',
|
||||||
|
placement: props.placement,
|
||||||
|
effect: props.effect,
|
||||||
|
value: visible.value,
|
||||||
|
manualMode: true,
|
||||||
|
popperClass: 'el-dropdown-popper',
|
||||||
|
trigger: props.trigger,
|
||||||
|
}, {
|
||||||
|
default: () => slots.dropdown?.(),
|
||||||
|
trigger: () => dropdownVnode,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.el-dropdown-popper {
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||||
|
}
|
||||||
|
.el-dropdown-popper.is-light {
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
}
|
||||||
|
.el-dropdown-popper.is-light .el-popper__arrow::before {
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
background: #FFF;
|
||||||
|
}
|
||||||
|
.el-dropdown-popper .el-dropdown-menu {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.el-dropdown-selfdefine {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
115
packages/dropdown/src/useDropdown.ts
Normal file
115
packages/dropdown/src/useDropdown.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import { inject, computed, ref } from 'vue'
|
||||||
|
import { generateId } from '@element-plus/utils/util'
|
||||||
|
import { eventKeys } from '@element-plus/utils/aria'
|
||||||
|
import { on, addClass } from '@element-plus/utils/dom'
|
||||||
|
import { IElDropdownInstance } from './dropdown'
|
||||||
|
|
||||||
|
export const useDropdown = () => {
|
||||||
|
const ELEMENT = null
|
||||||
|
const elDropdown = inject<IElDropdownInstance>('elDropdown', {})
|
||||||
|
const _elDropdownSize = computed(() => elDropdown?.dropdownSize)
|
||||||
|
|
||||||
|
return {
|
||||||
|
ELEMENT,
|
||||||
|
elDropdown,
|
||||||
|
_elDropdownSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initDropdownDomEvent = (dropdownChildren, triggerElm, _instance) => {
|
||||||
|
const menuItems = ref<Nullable<HTMLButtonElement[]>>(null)
|
||||||
|
const menuItemsArray = ref<Nullable<HTMLElement[]>>(null)
|
||||||
|
const dropdownElm = ref<Nullable<HTMLElement>>(null)
|
||||||
|
const listId = ref(`dropdown-menu-${generateId()}`)
|
||||||
|
dropdownElm.value = dropdownChildren?.subTree.el
|
||||||
|
|
||||||
|
function removeTabindex() {
|
||||||
|
triggerElm.setAttribute('tabindex', '-1')
|
||||||
|
menuItemsArray.value?.forEach(item => {
|
||||||
|
item.setAttribute('tabindex', '-1')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTabindex(ele) {
|
||||||
|
removeTabindex()
|
||||||
|
ele?.setAttribute('tabindex', '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTriggerKeyDown(ev: KeyboardEvent) {
|
||||||
|
/**
|
||||||
|
* https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/keyCode
|
||||||
|
* keyCode is deprecated, we should replace the api with event.key
|
||||||
|
* */
|
||||||
|
const keyCode = ev.keyCode
|
||||||
|
if ([eventKeys.up, eventKeys.down].includes(keyCode)) {
|
||||||
|
removeTabindex()
|
||||||
|
resetTabindex(menuItems.value[0])
|
||||||
|
menuItems.value[0].focus()
|
||||||
|
ev.preventDefault()
|
||||||
|
ev.stopPropagation()
|
||||||
|
} else if (keyCode === eventKeys.enter) {
|
||||||
|
_instance.handleClick()
|
||||||
|
} else if ([eventKeys.tab, eventKeys.esc].includes(keyCode)) {
|
||||||
|
_instance.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleItemKeyDown(ev) {
|
||||||
|
const keyCode = ev.keyCode
|
||||||
|
const target = ev.target
|
||||||
|
const currentIndex = menuItemsArray.value.indexOf(target)
|
||||||
|
const max = menuItemsArray.value.length - 1
|
||||||
|
let nextIndex
|
||||||
|
if ([eventKeys.up, eventKeys.down].includes(keyCode)) {
|
||||||
|
if (keyCode === eventKeys.up) {
|
||||||
|
nextIndex = currentIndex !== 0 ? currentIndex - 1 : 0
|
||||||
|
} else {
|
||||||
|
nextIndex = currentIndex < max ? currentIndex + 1 : max
|
||||||
|
}
|
||||||
|
removeTabindex()
|
||||||
|
resetTabindex(menuItems.value[nextIndex])
|
||||||
|
menuItems.value[nextIndex].focus()
|
||||||
|
ev.preventDefault()
|
||||||
|
ev.stopPropagation()
|
||||||
|
} else if (keyCode === eventKeys.enter) {
|
||||||
|
triggerElmFocus()
|
||||||
|
target.click()
|
||||||
|
if (_instance.props.hideOnClick) {
|
||||||
|
_instance.hide()
|
||||||
|
}
|
||||||
|
} else if ([eventKeys.tab, eventKeys.esc].includes(keyCode)) {
|
||||||
|
_instance.hide()
|
||||||
|
triggerElmFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAria() {
|
||||||
|
dropdownElm.value.setAttribute('id', listId.value)
|
||||||
|
triggerElm.setAttribute('aria-haspopup', 'list')
|
||||||
|
triggerElm.setAttribute('aria-controls', listId.value)
|
||||||
|
if (!_instance.props.splitButton) {
|
||||||
|
triggerElm.setAttribute('role', 'button')
|
||||||
|
triggerElm.setAttribute('tabindex', _instance.tabindex)
|
||||||
|
addClass(triggerElm, 'el-dropdown-selfdefine')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEvent() {
|
||||||
|
on(triggerElm, 'keydown', handleTriggerKeyDown)
|
||||||
|
on(dropdownElm.value, 'keydown', handleItemKeyDown, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function initDomOperation() {
|
||||||
|
menuItems.value = dropdownElm.value.querySelectorAll("[tabindex='-1']") as unknown as HTMLButtonElement[]
|
||||||
|
menuItemsArray.value = [].slice.call(menuItems.value)
|
||||||
|
|
||||||
|
initEvent()
|
||||||
|
initAria()
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerElmFocus() {
|
||||||
|
triggerElm?.focus?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
initDomOperation()
|
||||||
|
}
|
@ -6,6 +6,7 @@ import ElButton from '@element-plus/button'
|
|||||||
import ElBadge from '@element-plus/badge'
|
import ElBadge from '@element-plus/badge'
|
||||||
import ElCard from '@element-plus/card'
|
import ElCard from '@element-plus/card'
|
||||||
import ElCheckbox from '@element-plus/checkbox'
|
import ElCheckbox from '@element-plus/checkbox'
|
||||||
|
import ElDropdown from '@element-plus/dropdown'
|
||||||
import ElTag from '@element-plus/tag'
|
import ElTag from '@element-plus/tag'
|
||||||
import ElLayout from '@element-plus/layout'
|
import ElLayout from '@element-plus/layout'
|
||||||
import ElDivider from '@element-plus/divider'
|
import ElDivider from '@element-plus/divider'
|
||||||
@ -38,6 +39,7 @@ export {
|
|||||||
ElCard,
|
ElCard,
|
||||||
ElCheckbox,
|
ElCheckbox,
|
||||||
ElDivider,
|
ElDivider,
|
||||||
|
ElDropdown,
|
||||||
ElTag,
|
ElTag,
|
||||||
ElTimeline,
|
ElTimeline,
|
||||||
ElProgress,
|
ElProgress,
|
||||||
@ -66,6 +68,7 @@ export default function install(app: App): void {
|
|||||||
ElBadge(app)
|
ElBadge(app)
|
||||||
ElCard(app)
|
ElCard(app)
|
||||||
ElCheckbox(app)
|
ElCheckbox(app)
|
||||||
|
ElDropdown(app)
|
||||||
ElTag(app)
|
ElTag(app)
|
||||||
ElLayout(app)
|
ElLayout(app)
|
||||||
ElDivider(app)
|
ElDivider(app)
|
||||||
|
@ -11,9 +11,10 @@ export const on = function(
|
|||||||
element: HTMLElement | Document | Window,
|
element: HTMLElement | Document | Window,
|
||||||
event: string,
|
event: string,
|
||||||
handler: EventListenerOrEventListenerObject,
|
handler: EventListenerOrEventListenerObject,
|
||||||
|
useCapture = false,
|
||||||
): void {
|
): void {
|
||||||
if (element && event && handler) {
|
if (element && event && handler) {
|
||||||
element.addEventListener(event, handler, false)
|
element.addEventListener(event, handler, useCapture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class MenuItem {
|
|||||||
case keys.space: {
|
case keys.space: {
|
||||||
prevDef = true
|
prevDef = true
|
||||||
|
|
||||||
(event.currentTarget as HTMLElement).click()
|
;(event.currentTarget as HTMLElement).click()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user