Merge branch 'dev' into renovate/node-22.x

This commit is contained in:
qiang 2024-11-14 23:28:39 +08:00 committed by GitHub
commit 5b4b6d60e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 463 additions and 229 deletions

View File

@ -16,4 +16,4 @@ jobs:
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
labels: 'inactive'
labels: 'inactive, needs more info'

View File

@ -7,6 +7,7 @@ import VPApp, { NotFound, globals } from '../vitepress'
import { define } from '../utils/types'
import 'uno.css'
import './style.css'
import 'vitepress/dist/client/theme-default/styles/components/vp-code-group.css'
import type { Theme } from 'vitepress'
export default define<Theme>({

View File

@ -13,6 +13,7 @@ import SponsorRightLogoSmallList from '../sponsors/right-logo-small-list.vue'
const headers = useToc()
const lang = useLang()
const sponsor = computed(() => sponsorLocale[lang.value])
const removeTag = (str: string) => str.replace(/<span.*<\/span>/g, '')
</script>
<template>
@ -26,7 +27,7 @@ const sponsor = computed(() => sponsorLocale[lang.value])
:href="link"
:title="text"
>
<div v-html="text" />
<div :title="removeTag(text)" v-html="text" />
<template v-if="children" #sub-link>
<el-anchor-link
v-for="{ link: childLink, text: childText } in children"
@ -34,7 +35,7 @@ const sponsor = computed(() => sponsorLocale[lang.value])
:href="childLink"
:title="text"
>
<div v-html="childText" />
<div :title="removeTag(childText)" v-html="childText" />
</el-anchor-link>
</template>
</el-anchor-link>
@ -59,4 +60,11 @@ const sponsor = computed(() => sponsorLocale[lang.value])
width: 100%;
}
}
.el-anchor__item {
.el-anchor__link > div {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>

View File

@ -4,6 +4,7 @@ import { onMounted } from 'vue'
import nprogress from 'nprogress'
// import dayjs from 'dayjs'
import { isClient, useEventListener, useToggle } from '@vueuse/core'
import { EVENT_CODE } from 'element-plus'
import { useSidebar } from '../composables/sidebar'
import { useToggleWidgets } from '../composables/toggle-widgets'
// import { useLang } from '../composables/lang'
@ -36,7 +37,7 @@ useToggleWidgets(isSidebarOpen, () => {
useEventListener('keydown', (e) => {
if (!isClient) return
if (e.key === 'Escape' && isSidebarOpen.value) {
if (e.code === EVENT_CODE.esc && isSidebarOpen.value) {
toggleSidebar(false)
document.querySelector<HTMLButtonElement>('.sidebar-button')?.focus()
}

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed, getCurrentInstance, ref, toRef } from 'vue'
import { isClient, useClipboard, useToggle } from '@vueuse/core'
import { EVENT_CODE } from 'element-plus'
import { CaretTop } from '@element-plus/icons-vue'
import { useLang } from '../composables/lang'
import { useSourceCode } from '../composables/source-code'
@ -38,7 +39,11 @@ const onPlaygroundClick = () => {
}
const onSourceVisibleKeydown = (e: KeyboardEvent) => {
if (['Enter', 'Space'].includes(e.code)) {
if (
[EVENT_CODE.enter, EVENT_CODE.numpadEnter, EVENT_CODE.space].includes(
e.code
)
) {
e.preventDefault()
toggleSourceVisible(false)
sourceCodeRef.value?.focus()

View File

@ -51,14 +51,15 @@ descriptions/customized-style
### Descriptions Attributes
| Name | Description | Type | Default |
| --------- | ------------------------------------------ | ---------------------------------------------- | ---------- |
| border | with or without border | ^[boolean] | false |
| column | numbers of `Descriptions Item` in one line | ^[number] | 3 |
| direction | direction of list | ^[enum]`'vertical' \| 'horizontal'` | horizontal |
| size | size of list | ^[enum]`'' \| 'large' \| 'default' \| 'small'` | — |
| title | title text, display on the top left | ^[string] | '' |
| extra | extra text, display on the top right | ^[string] | '' |
| Name | Description | Type | Default |
| -------------------- | ------------------------------------------ | ---------------------------------------------- | ---------- |
| border | with or without border | ^[boolean] | false |
| column | numbers of `Descriptions Item` in one line | ^[number] | 3 |
| direction | direction of list | ^[enum]`'vertical' \| 'horizontal'` | horizontal |
| size | size of list | ^[enum]`'' \| 'large' \| 'default' \| 'small'` | — |
| title | title text, display on the top left | ^[string] | '' |
| extra | extra text, display on the top right | ^[string] | '' |
| label-width ^(2.8.8) | label width of every column | ^[string] / ^[number] | '' |
### Descriptions Slots
@ -72,17 +73,18 @@ descriptions/customized-style
### DescriptionsItem Attributes
| Name | Description | Type | Default |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------- |
| label | label text | ^[string] | '' |
| span | colspan of column | ^[number] | 1 |
| rowspan ^(2.8.1) | the number of rows a cell should span | ^[number] | 1 |
| width | column width, the width of the same column in different rows is set by the max value (If no `border`, width contains label and content) | ^[string] / ^[number] | '' |
| min-width | column minimum width, columns with `width` has a fixed width, while columns with `min-width` has a width that is distributed in proportion (If no`border`, width contains label and content) | ^[string] / ^[number] | '' |
| align | column content alignment (If no `border`, effective for both label and content) | ^[enum]`'left' \| 'center' \| 'right'` | left |
| label-align | column label alignment, if omitted, the value of the above `align` attribute will be applied (If no `border`, please use `align` attribute) | ^[enum]`'left' \| 'center' \| 'right'` | '' |
| class-name | column content custom class name | ^[string] | '' |
| label-class-name | column label custom class name | ^[string] | '' |
| Name | Description | Type | Default |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------- |
| label | label text | ^[string] | '' |
| span | colspan of column | ^[number] | 1 |
| rowspan ^(2.8.1) | the number of rows a cell should span | ^[number] | 1 |
| width | column width, the width of the same column in different rows is set by the max value (If no `border`, width contains label and content) | ^[string] / ^[number] | '' |
| min-width | column minimum width, columns with `width` has a fixed width, while columns with `min-width` has a width that is distributed in proportion (If no`border`, width contains label and content) | ^[string] / ^[number] | '' |
| label-width ^(2.8.8) | column label width, if not set, it will be the same as the width of the column. Higher priority than the `label-width` of `Descriptions` | ^[string] / ^[number] | '' |
| align | column content alignment (If no `border`, effective for both label and content) | ^[enum]`'left' \| 'center' \| 'right'` | left |
| label-align | column label alignment, if omitted, the value of the above `align` attribute will be applied (If no `border`, please use `align` attribute) | ^[enum]`'left' \| 'center' \| 'right'` | '' |
| class-name | column content custom class name | ^[string] | '' |
| label-class-name | column label custom class name | ^[string] | '' |
### DescriptionsItem Slots

View File

@ -17,16 +17,21 @@ Element Plus provides a set of common icons.
### Using packaging manager
```shell
# Choose a package manager you like.
Choose a package manager you like.
# NPM
::: code-group
```shell [npm]
$ npm install @element-plus/icons-vue
# Yarn
```
```shell [yarn]
$ yarn add @element-plus/icons-vue
# pnpm
```
```shell [pnpm]
$ pnpm install @element-plus/icons-vue
```
:::
### Register All Icons

View File

@ -39,7 +39,7 @@ page-header/custom-icon
## No icon
Sometimes the page is just full of elements, and you might not want the icon to show up on the page,
you can set the `icon` attribute to `null` to get rid of it.
you can set the `icon` attribute to `""` to get rid of it.
:::demo

View File

@ -254,6 +254,8 @@ select-v2/custom-label
| remote | whether search data from server | ^[boolean] | false |
| remote-method | function that gets called when the input value changes. Its parameter is the current input value. To use this, `filterable` must be true | ^[Function]`(keyword: string) => void` | — |
| validate-event | whether to trigger form validation | ^[boolean] | true |
| offset ^(2.8.8) | offset of the dropdown | ^[number] | 12 |
| show-arrow ^(2.8.8) | whether the dropdown has an arrow | ^[boolean] | true |
| placement | position of dropdown | ^[enum]`'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end' \| 'right' \| 'right-start' \| 'right-end'` | bottom-start |
| fallback-placements ^(2.5.6) | list of possible positions for dropdown [popper.js](https://popper.js.org/docs/v2/modifiers/flip/#fallbackplacements) | ^[array]`Placement[]` | ['bottom-start', 'top-start', 'right', 'left'] |
| collapse-tags-tooltip ^(2.3.0) | whether show all selected tags when mouse hover text of collapse-tags. To use this, `collapse-tags` must be true | ^[boolean] | false |

View File

@ -223,6 +223,8 @@ select/custom-label
| tag-type | tag type | ^[enum]`'' \| 'success' \| 'info' \| 'warning' \| 'danger'` | info |
| tag-effect ^(2.7.7) | tag effect | ^[enum]`'' \| 'light' \| 'dark' \| 'plain'` | light |
| validate-event | whether to trigger form validation | ^[boolean] | true |
| offset ^(2.8.8) | offset of the dropdown | ^[number] | 12 |
| show-arrow ^(2.8.8) | whether the dropdown has an arrow | ^[boolean] | true |
| placement ^(2.2.17) | position of dropdown | ^[enum]`'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end' \| 'right' \| 'right-start' \| 'right-end'` | bottom-start |
| fallback-placements ^(2.5.6) | list of possible positions for dropdown [popper.js](https://popper.js.org/docs/v2/modifiers/flip/#fallbackplacements) | ^[array]`Placement[]` | ['bottom-start', 'top-start', 'right', 'left'] |
| max-collapse-tags ^(2.3.0) | the max tags number to be shown. To use this, `collapse-tags` must be true | ^[number] | 1 |

View File

@ -44,19 +44,24 @@ In addition, every commit and PR on the dev branch will be published to [pkg.pr.
so that you can utilize bundlers like [Vite](https://vitejs.dev) and
[webpack](https://webpack.js.org/).
```shell
# Choose a package manager you like.
# NPM
Choose a package manager you like.
::: code-group
```shell [npm]
$ npm install element-plus --save
# Yarn
```
```shell [yarn]
$ yarn add element-plus
```
# pnpm
```shell [pnpm]
$ pnpm install element-plus
```
:::
If your network environment is not good, it is recommended to use a mirror registry [cnpm](https://github.com/cnpm/cnpm) or [npmmirror](https://npmmirror.com/).
```shell

View File

@ -1,5 +1,5 @@
<template>
<el-page-header :icon="null">
<el-page-header icon="">
<template #content>
<div class="flex items-center">
<el-avatar

View File

@ -1,5 +1,5 @@
<template>
<el-page-header :icon="null">
<el-page-header icon="">
<template #content>
<span class="text-large font-600 mr-3"> Title </span>
</template>

View File

@ -324,6 +324,7 @@ export default defineComponent({
break
}
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
checkNode(target)
break
}

View File

@ -547,6 +547,7 @@ const handleKeyDown = (e: KeyboardEvent) => {
switch (e.code) {
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
togglePopperVisible()
break
case EVENT_CODE.down:
@ -611,6 +612,7 @@ const handleSuggestionKeyDown = (e: KeyboardEvent) => {
break
}
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
target.click()
break
}

View File

@ -325,6 +325,7 @@ function handleEsc(event: KeyboardEvent) {
function handleKeyDown(event: KeyboardEvent) {
switch (event.code) {
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
case EVENT_CODE.space:
event.preventDefault()
event.stopPropagation()

View File

@ -204,4 +204,50 @@ describe('Descriptions.vue', () => {
await nextTick()
expect(wrapper.findComponent(ElTag).text()).toBe(CHANGE_VALUE)
})
test('should render labelWidth prop of DescriptionsItem', () => {
const wrapper = mount(() => (
<ElDescriptions border>
{Array.from({ length: 3 }).map(() => (
<ElDescriptionsItem label="测试标签" labelWidth="150px" />
))}
</ElDescriptions>
))
expect(
wrapper.find('.el-descriptions__label').attributes('style')
).toContain('width: 150px')
})
test('should render labelWidth prop of Descriptions', () => {
const wrapper = mount(() => (
<ElDescriptions label-width="150px" border>
{Array.from({ length: 3 }).map(() => (
<ElDescriptionsItem label="测试标签" />
))}
</ElDescriptions>
))
expect(
wrapper.find('.el-descriptions__label').attributes('style')
).toContain('width: 150px')
})
test('should render labelWidth prop of Descriptions and DescriptionsItem with higher priority', () => {
const wrapper = mount(() => (
<ElDescriptions label-width="100px" border>
<ElDescriptionsItem label="测试标签" />
{Array.from({ length: 2 }).map(() => (
<ElDescriptionsItem label="测试标签" label-width="150px" />
))}
</ElDescriptions>
))
expect(
wrapper.findAll('.el-descriptions__label')[0].attributes('style')
).toContain('width: 100px')
expect(
wrapper.findAll('.el-descriptions__label')[1].attributes('style')
).toContain('width: 150px')
})
})

View File

@ -39,6 +39,13 @@ export const descriptionItemProps = buildProps({
type: [String, Number],
default: '',
},
/**
* @description column label width, if not set, it will be the same as the width of the column. Higher priority than the `label-width` of `Descriptions`
*/
labelWidth: {
type: [String, Number],
default: '',
},
/**
* @description column content alignment (If no `border`, effective for both label and content)
*/

View File

@ -42,6 +42,13 @@ export const descriptionProps = buildProps({
type: String,
default: '',
},
/**
* @description width of every label column
*/
labelWidth: {
type: [String, Number],
default: '',
},
} as const)
export type DescriptionProps = ExtractPropTypes<typeof descriptionProps>

View File

@ -52,8 +52,13 @@ export default defineComponent({
const labelAlign = item.labelAlign ? `is-${item.labelAlign}` : align
const className = item.className
const labelClassName = item.labelClassName
const width =
this.type === 'label'
? item.labelWidth || this.descriptions.labelWidth || item.width
: item.width
const style = {
width: addUnit(item.width),
width: addUnit(width),
minWidth: addUnit(item.minWidth),
}
const ns = useNamespace('descriptions')

View File

@ -7,6 +7,7 @@ export interface IDescriptionsInject {
size: ComponentSize
title: string
extra: string
labelWidth: string | number
}
export interface IDescriptionsItemInject {
@ -15,6 +16,7 @@ export interface IDescriptionsItemInject {
rowspan: number
width: string | number
minWidth: string | number
labelWidth: string | number
align: string
labelAlign: string
className: string

View File

@ -89,8 +89,11 @@ export default defineComponent({
})
const handleKeydown = composeEventHandlers((e: KeyboardEvent) => {
const { code } = e
if (code === EVENT_CODE.enter || code === EVENT_CODE.space) {
if (
[EVENT_CODE.enter, EVENT_CODE.numpadEnter, EVENT_CODE.space].includes(
e.code
)
) {
e.preventDefault()
e.stopImmediatePropagation()
emit('clickimpl', e)

View File

@ -148,7 +148,12 @@ export default defineComponent({
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 triggerKeys = [
EVENT_CODE.enter,
EVENT_CODE.numpadEnter,
EVENT_CODE.space,
EVENT_CODE.down,
]
const wrapStyle = computed<CSSProperties>(() => ({
maxHeight: addUnit(props.maxHeight),

View File

@ -46,7 +46,7 @@ export const initDropdownDomEvent = (
menuItems.value[0].focus()
ev.preventDefault()
ev.stopPropagation()
} else if (code === EVENT_CODE.enter) {
} else if ([EVENT_CODE.enter, EVENT_CODE.numpadEnter].includes(code)) {
_instance.handleClick()
} else if ([EVENT_CODE.tab, EVENT_CODE.esc].includes(code)) {
_instance.hide()

View File

@ -141,7 +141,7 @@ describe('<ElFocusTrap', () => {
expect(wrapper.emitted('release-requested')).toBeFalsy()
focusContainer?.trigger('keydown', {
key: EVENT_CODE.esc,
code: EVENT_CODE.esc,
})
await nextTick()
@ -155,7 +155,7 @@ describe('<ElFocusTrap', () => {
await nextTick()
focusContainer?.trigger('keydown', {
key: EVENT_CODE.esc,
code: EVENT_CODE.esc,
})
// Expect no emit if esc while layer paused
@ -173,13 +173,13 @@ describe('<ElFocusTrap', () => {
expect(wrapper.emitted('focusout-prevented')).toBeFalsy()
await childComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0)?.element)
expect(wrapper.emitted('focusout-prevented')?.length).toBe(2)
;(items.at(2)?.element as HTMLElement).focus()
await childComponent.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(wrapper.emitted('focusout-prevented')?.length).toBe(4)
})
@ -203,14 +203,14 @@ describe('<ElFocusTrap', () => {
*/
// when loop is off
await childComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0)?.element)
;(items.at(2)?.element as HTMLElement).focus()
expect(document.activeElement).toBe(items.at(2)?.element)
await childComponent.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(2)?.element)
@ -220,12 +220,12 @@ describe('<ElFocusTrap', () => {
})
await childComponent.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0)?.element)
await childComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(2)?.element)
})
@ -239,7 +239,7 @@ describe('<ElFocusTrap', () => {
expect(document.activeElement).toBe(focusComponent.element)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(focusComponent.element)
})
@ -268,7 +268,7 @@ describe('<ElFocusTrap', () => {
expect(document.activeElement).toBe(beforeTrap.element)
await focusContainer.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
preventDefault,
})
if (!isDefaultPrevented) {
@ -278,7 +278,7 @@ describe('<ElFocusTrap', () => {
expect(document.activeElement).toBe(items.at(0)?.element)
await focusContainer.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
preventDefault,
})
if (!isDefaultPrevented) {
@ -303,7 +303,7 @@ describe('<ElFocusTrap', () => {
expect(document.activeElement).toBe(items.at(0)?.element)
await focusComponent.trigger('keydown.shift', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(2)?.element)
@ -313,7 +313,7 @@ describe('<ElFocusTrap', () => {
expect(document.activeElement).toBe(newFocusTrap.find('.item').element)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).not.toBe(items.at(0)?.element)
newFocusTrap.unmount()
@ -322,7 +322,7 @@ describe('<ElFocusTrap', () => {
expect(document.activeElement).toBe(items.at(2)?.element)
await focusComponent.trigger('keydown', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(document.activeElement).toBe(items.at(0)?.element)
})

View File

@ -85,10 +85,10 @@ export default defineComponent({
if (!props.loop && !props.trapped) return
if (focusLayer.paused) return
const { key, altKey, ctrlKey, metaKey, currentTarget, shiftKey } = e
const { code, altKey, ctrlKey, metaKey, currentTarget, shiftKey } = e
const { loop } = props
const isTabbing =
key === EVENT_CODE.tab && !altKey && !ctrlKey && !metaKey
code === EVENT_CODE.tab && !altKey && !ctrlKey && !metaKey
const currentFocusingEl = document.activeElement
if (isTabbing && currentFocusingEl) {

View File

@ -18,7 +18,7 @@
v-for="(item, index) in options"
:id="`${contentId}-${index}`"
ref="optionRefs"
:key="item.value"
:key="index"
:class="optionkls(item, index)"
role="option"
:aria-disabled="item.disabled || disabled || undefined"

View File

@ -61,7 +61,7 @@ import { pick } from 'lodash-unified'
import { useFocusController, useId, useNamespace } from '@element-plus/hooks'
import ElInput, { inputProps } from '@element-plus/components/input'
import ElTooltip from '@element-plus/components/tooltip'
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { EVENT_CODE, UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { useFormDisabled } from '@element-plus/components/form'
import { isFunction } from '@element-plus/utils'
import { mentionEmits, mentionProps } from './mention'
@ -125,54 +125,63 @@ const handleInputChange = (value: string) => {
syncAfterCursorMove()
}
const handleInputKeyDown = (e: KeyboardEvent | Event) => {
if (!('key' in e)) return
if (elInputRef.value?.isComposing) return
if (['ArrowLeft', 'ArrowRight'].includes(e.key)) {
syncAfterCursorMove()
} else if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
if (!visible.value) return
e.preventDefault()
const direction = e.key === 'ArrowUp' ? 'prev' : 'next'
dropdownRef.value?.navigateOptions(direction)
} else if (['Enter'].includes(e.key)) {
if (!visible.value) return
e.preventDefault()
if (dropdownRef.value?.hoverOption) {
dropdownRef.value?.selectHoverOption()
} else {
visible.value = false
}
} else if (['Escape'].includes(e.key)) {
if (!visible.value) return
e.preventDefault()
visible.value = false
} else if (['Backspace'].includes(e.key)) {
if (props.whole && mentionCtx.value) {
const { splitIndex, selectionEnd, pattern, prefixIndex, prefix } =
mentionCtx.value
const inputEl = getInputEl()
if (!inputEl) return
const inputValue = inputEl.value
const matchOption = props.options.find((item) => item.value === pattern)
const isWhole = isFunction(props.checkIsWhole)
? props.checkIsWhole(pattern, prefix)
: matchOption
if (isWhole && splitIndex !== -1 && splitIndex + 1 === selectionEnd) {
e.preventDefault()
const newValue =
inputValue.slice(0, prefixIndex) + inputValue.slice(splitIndex + 1)
emit(UPDATE_MODEL_EVENT, newValue)
const handleInputKeyDown = (event: KeyboardEvent | Event) => {
if (!('code' in event) || elInputRef.value?.isComposing) return
const newSelectionEnd = prefixIndex
nextTick(() => {
// input value is updated
inputEl.selectionStart = newSelectionEnd
inputEl.selectionEnd = newSelectionEnd
syncDropdownVisible()
})
switch (event.code) {
case EVENT_CODE.left:
case EVENT_CODE.right:
syncAfterCursorMove()
break
case EVENT_CODE.up:
case EVENT_CODE.down:
if (!visible.value) return
event.preventDefault()
dropdownRef.value?.navigateOptions(
event.code === EVENT_CODE.up ? 'prev' : 'next'
)
break
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
if (!visible.value) return
event.preventDefault()
if (dropdownRef.value?.hoverOption) {
dropdownRef.value?.selectHoverOption()
} else {
visible.value = false
}
break
case EVENT_CODE.esc:
if (!visible.value) return
event.preventDefault()
visible.value = false
break
case EVENT_CODE.backspace:
if (props.whole && mentionCtx.value) {
const { splitIndex, selectionEnd, pattern, prefixIndex, prefix } =
mentionCtx.value
const inputEl = getInputEl()
if (!inputEl) return
const inputValue = inputEl.value
const matchOption = props.options.find((item) => item.value === pattern)
const isWhole = isFunction(props.checkIsWhole)
? props.checkIsWhole(pattern, prefix)
: matchOption
if (isWhole && splitIndex !== -1 && splitIndex + 1 === selectionEnd) {
event.preventDefault()
const newValue =
inputValue.slice(0, prefixIndex) + inputValue.slice(splitIndex + 1)
emit(UPDATE_MODEL_EVENT, newValue)
const newSelectionEnd = prefixIndex
nextTick(() => {
// input value is updated
inputEl.selectionStart = newSelectionEnd
inputEl.selectionEnd = newSelectionEnd
syncDropdownVisible()
})
}
}
}
}
}

View File

@ -41,6 +41,7 @@ class MenuItem {
break
}
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
case EVENT_CODE.space: {
prevDef = true
;(event.currentTarget as HTMLElement).click()

View File

@ -47,6 +47,7 @@ class SubMenu {
break
}
case EVENT_CODE.enter:
case EVENT_CODE.numpadEnter:
case EVENT_CODE.space: {
prevDef = true
;(event.currentTarget as HTMLElement).click()

View File

@ -1,3 +1,4 @@
import '@element-plus/components/base/style/css'
import '@element-plus/theme-chalk/el-pagination.css'
import '@element-plus/components/select/style/css'
import '@element-plus/components/input/style/css'

View File

@ -1,3 +1,4 @@
import '@element-plus/components/base/style'
import '@element-plus/theme-chalk/src/pagination.scss'
import '@element-plus/components/select/style'
import '@element-plus/components/input/style'

View File

@ -149,34 +149,34 @@ describe('<ElRovingFocusItem />', () => {
const firstDOMItem = DOMItems.at(0)
expect(onItemShiftTab).not.toHaveBeenCalled()
await firstDOMItem.trigger('keydown.shift', {
key: EVENT_CODE.tab,
code: EVENT_CODE.tab,
})
expect(items.at(0).emitted()).toHaveProperty('keydown')
expect(onItemShiftTab).toHaveBeenCalled()
// navigating clockwise
expect(document.activeElement).toBe(document.body)
await DOMItems.at(1).trigger('keydown', {
key: EVENT_CODE.down,
code: EVENT_CODE.down,
})
await nextTick()
expect(document.activeElement).toStrictEqual(DOMItems.at(2).element)
// navigate anticlockwise
await DOMItems.at(1).trigger('keydown', {
key: EVENT_CODE.up,
code: EVENT_CODE.up,
})
await nextTick()
expect(document.activeElement).toStrictEqual(DOMItems.at(0).element)
// should be able to focus on the last element when press End
await DOMItems.at(0).trigger('keydown', {
key: EVENT_CODE.end,
code: EVENT_CODE.end,
})
await nextTick()
expect(document.activeElement).toStrictEqual(DOMItems.at(2).element)
await DOMItems.at(0).trigger('keydown', {
key: EVENT_CODE.home,
code: EVENT_CODE.home,
})
await nextTick()
expect(document.activeElement).toStrictEqual(DOMItems.at(0).element)

View File

@ -7,7 +7,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.enter,
code: EVENT_CODE.enter,
})
)
).toBe(undefined)
@ -15,7 +15,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.left,
code: EVENT_CODE.left,
})
)
).toBe('prev')
@ -23,7 +23,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.left,
code: EVENT_CODE.left,
}),
'vertical'
)
@ -31,7 +31,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.up,
code: EVENT_CODE.up,
}),
'horizontal'
)
@ -40,7 +40,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.left,
code: EVENT_CODE.left,
}),
'horizontal',
'rtl'
@ -50,7 +50,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.right,
code: EVENT_CODE.right,
}),
'horizontal',
'rtl'
@ -60,7 +60,7 @@ describe('util', () => {
expect(
Util.getFocusIntent(
new KeyboardEvent('mousedown', {
key: EVENT_CODE.up,
code: EVENT_CODE.up,
}),
'vertical',
'rtl'

View File

@ -87,8 +87,8 @@ export default defineComponent({
emit('keydown', e)
},
(e) => {
const { key, shiftKey, target, currentTarget } = e as KeyboardEvent
if (key === EVENT_CODE.tab && shiftKey) {
const { code, shiftKey, target, currentTarget } = e as KeyboardEvent
if (code === EVENT_CODE.tab && shiftKey) {
onItemShiftTab()
return
}

View File

@ -34,7 +34,7 @@ export const getFocusIntent = (
orientation?: Orientation,
dir?: Direction
) => {
const key = getDirectionAwareKey(event.key, dir)
const key = getDirectionAwareKey(event.code, dir)
if (
orientation === 'vertical' &&
[EVENT_CODE.left, EVENT_CODE.right].includes(key)

View File

@ -237,6 +237,20 @@ export const SelectProps = buildProps({
type: Boolean,
default: true,
},
/**
* @description offset of the dropdown
*/
offset: {
type: Number,
default: 12,
},
/**
* @description Determines whether the arrow is displayed
*/
showArrow: {
type: Boolean,
default: true,
},
/**
* @description position of dropdown
*/

View File

@ -226,7 +226,7 @@ export default defineComponent({
const onKeydown = (e: KeyboardEvent) => {
const { code } = e
const { tab, esc, down, up, enter } = EVENT_CODE
const { tab, esc, down, up, enter, numpadEnter } = EVENT_CODE
if (code !== tab) {
e.preventDefault()
e.stopPropagation()
@ -234,22 +234,19 @@ export default defineComponent({
switch (code) {
case tab:
case esc: {
case esc:
onEscOrTab()
break
}
case down: {
case down:
onForward()
break
}
case up: {
case up:
onBackward()
break
}
case enter: {
case enter:
case numpadEnter:
onKeyboardSelect()
break
}
}
}

View File

@ -22,6 +22,8 @@
trigger="click"
:persistent="persistent"
:append-to="appendTo"
:show-arrow="showArrow"
:offset="offset"
@before-show="handleMenuEnter"
@hide="states.isBeforeHide = false"
>
@ -227,7 +229,7 @@
<component :is="clearIcon" />
</el-icon>
<el-icon
v-if="validateState && validateIcon"
v-if="validateState && validateIcon && needStatusIcon"
:class="[nsInput.e('icon'), nsInput.e('validateIcon')]"
>
<component :is="validateIcon" />

View File

@ -135,6 +135,8 @@ const useSelect = (props: ISelectV2Props, emit: SelectEmitFn) => {
const selectDisabled = computed(() => props.disabled || elForm?.disabled)
const needStatusIcon = computed(() => elForm?.statusIcon ?? false)
const popupHeight = computed(() => {
const totalHeight = filteredOptions.value.length * props.itemHeight
return totalHeight > props.height ? props.height : totalHeight
@ -812,9 +814,11 @@ const useSelect = (props: ISelectV2Props, emit: SelectEmitFn) => {
watch(
() => props.modelValue,
(val, oldVal) => {
const isValEmpty = !val || (isArray(val) && val.length === 0)
if (
!val ||
(props.multiple && val.toString() !== states.previousValue) ||
isValEmpty ||
(props.multiple && !isEqual(val.toString(), states.previousValue)) ||
(!props.multiple &&
getValueKey(val) !== getValueKey(states.previousValue))
) {
@ -913,6 +917,7 @@ const useSelect = (props: ISelectV2Props, emit: SelectEmitFn) => {
shouldShowPlaceholder,
selectDisabled,
selectSize,
needStatusIcon,
showClearBtn,
states,
isFocused,

View File

@ -209,6 +209,20 @@ export const SelectProps = buildProps({
* @description in remote search method show suffix icon
*/
remoteShowSuffix: Boolean,
/**
* @description determines whether the arrow is displayed
*/
showArrow: {
type: Boolean,
default: true,
},
/**
* @description offset of the dropdown
*/
offset: {
type: Number,
default: 12,
},
/**
* @description position of dropdown
*/

View File

@ -22,6 +22,8 @@
:gpu-acceleration="false"
:persistent="persistent"
:append-to="appendTo"
:show-arrow="showArrow"
:offset="offset"
@before-show="handleMenuEnter"
@hide="states.isBeforeHide = false"
>
@ -227,7 +229,7 @@
<component :is="clearIcon" />
</el-icon>
<el-icon
v-if="validateState && validateIcon"
v-if="validateState && validateIcon && needStatusIcon"
:class="[nsInput.e('icon'), nsInput.e('validateIcon')]"
>
<component :is="validateIcon" />

View File

@ -25,9 +25,9 @@ import {
isIOS,
isNumber,
isObject,
isPlainObject,
isUndefined,
scrollIntoView,
toRawType,
} from '@element-plus/utils'
import {
CHANGE_EVENT,
@ -141,6 +141,8 @@ export const useSelect = (props: ISelectProps, emit) => {
: !isEmptyValue(props.modelValue)
})
const needStatusIcon = computed(() => form?.statusIcon ?? false)
const showClose = computed(() => {
return (
props.clearable &&
@ -424,9 +426,7 @@ export const useSelect = (props: ISelectProps, emit) => {
const getOption = (value) => {
let option
const isObjectValue = toRawType(value).toLowerCase() === 'object'
const isNull = toRawType(value).toLowerCase() === 'null'
const isUndefined = toRawType(value).toLowerCase() === 'undefined'
const isObjectValue = isPlainObject(value)
for (let i = states.cachedOptions.size - 1; i >= 0; i--) {
const cachedOption = cachedOptionsArray.value[i]
@ -445,11 +445,7 @@ export const useSelect = (props: ISelectProps, emit) => {
}
}
if (option) return option
const label = isObjectValue
? value.label
: !isNull && !isUndefined
? value
: ''
const label = isObjectValue ? value.label : value ?? ''
const newOption = {
value,
currentLabel: label,
@ -595,7 +591,8 @@ export const useSelect = (props: ISelectProps, emit) => {
}
const getValueIndex = (arr: any[] = [], option) => {
if (!isObject(option?.value)) return arr.indexOf(option.value)
if (isUndefined(option)) return -1
if (!isObject(option.value)) return arr.indexOf(option.value)
return arr.findIndex((item) => {
return isEqual(get(item, props.valueKey), getValueKey(option))
@ -831,6 +828,7 @@ export const useSelect = (props: ISelectProps, emit) => {
shouldShowPlaceholder,
currentPlaceholder,
mouseEnterEventName,
needStatusIcon,
showClose,
iconComponent,
iconReverse,

View File

@ -197,13 +197,13 @@ describe('Slider', () => {
const slider = wrapper.findComponent({ name: 'ElSliderButton' })
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { key: EVENT_CODE.right })
new KeyboardEvent('keydown', { code: EVENT_CODE.right })
)
await nextTick()
expect(value.value).toBe(1)
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { key: EVENT_CODE.left })
new KeyboardEvent('keydown', { code: EVENT_CODE.left })
)
await nextTick()
expect(value.value).toBe(0)
@ -215,12 +215,12 @@ describe('Slider', () => {
const slider = wrapper.findComponent({ name: 'ElSliderButton' })
slider.vm.onKeyDown(new KeyboardEvent('keydown', { key: EVENT_CODE.up }))
slider.vm.onKeyDown(new KeyboardEvent('keydown', { code: EVENT_CODE.up }))
await nextTick()
expect(value.value).toBe(1)
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { key: EVENT_CODE.down })
new KeyboardEvent('keydown', { code: EVENT_CODE.down })
)
await nextTick()
expect(value.value).toBe(0)
@ -234,13 +234,13 @@ describe('Slider', () => {
const slider = wrapper.findComponent({ name: 'ElSliderButton' })
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { key: EVENT_CODE.pageUp })
new KeyboardEvent('keydown', { code: EVENT_CODE.pageUp })
)
await nextTick()
expect(value.value).toBe(3)
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { key: EVENT_CODE.pageDown })
new KeyboardEvent('keydown', { code: EVENT_CODE.pageDown })
)
await nextTick()
expect(value.value).toBe(-1)
@ -254,12 +254,14 @@ describe('Slider', () => {
const slider = wrapper.findComponent({ name: 'ElSliderButton' })
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { key: EVENT_CODE.home })
new KeyboardEvent('keydown', { code: EVENT_CODE.home })
)
await nextTick()
expect(value.value).toBe(-5)
slider.vm.onKeyDown(new KeyboardEvent('keydown', { key: EVENT_CODE.end }))
slider.vm.onKeyDown(
new KeyboardEvent('keydown', { code: EVENT_CODE.end })
)
await nextTick()
expect(value.value).toBe(10)
})

View File

@ -13,8 +13,6 @@ import type {
} from '../button'
import type { TooltipInstance } from '@element-plus/components/tooltip'
const { left, down, right, up, home, end, pageUp, pageDown } = EVENT_CODE
const useTooltip = (
props: SliderButtonProps,
formatTooltip: Ref<SliderProps['formatTooltip']>,
@ -151,21 +149,33 @@ export const useSliderButton = (
const onKeyDown = (event: KeyboardEvent) => {
let isPreventDefault = true
if ([left, down].includes(event.key)) {
onLeftKeyDown()
} else if ([right, up].includes(event.key)) {
onRightKeyDown()
} else if (event.key === home) {
onHomeKeyDown()
} else if (event.key === end) {
onEndKeyDown()
} else if (event.key === pageDown) {
onPageDownKeyDown()
} else if (event.key === pageUp) {
onPageUpKeyDown()
} else {
isPreventDefault = false
switch (event.code) {
case EVENT_CODE.left:
case EVENT_CODE.down:
onLeftKeyDown()
break
case EVENT_CODE.right:
case EVENT_CODE.up:
onRightKeyDown()
break
case EVENT_CODE.home:
onHomeKeyDown()
break
case EVENT_CODE.end:
onEndKeyDown()
break
case EVENT_CODE.pageDown:
onPageDownKeyDown()
break
case EVENT_CODE.pageUp:
onPageUpKeyDown()
break
default:
isPreventDefault = false
break
}
isPreventDefault && event.preventDefault()
}

View File

@ -77,9 +77,21 @@ function useWatcher<T>() {
const sortOrder = ref(null)
const hoverRow = ref(null)
watch(data, () => instance.state && scheduleLayout(false), {
deep: true,
})
watch(
data,
() => {
if (instance.state) {
scheduleLayout(false)
const needUpdateFixed = instance.props.tableLayout === 'auto'
if (needUpdateFixed) {
instance.refs.tableHeaderRef?.updateFixedColumnStyle()
}
}
},
{
deep: true,
}
)
// 检查 rowKey 是否存在
const assertRowKey = () => {

View File

@ -67,13 +67,38 @@ export default defineComponent({
const ns = useNamespace('table')
const filterPanels = ref({})
const { onColumnsChange, onScrollableChange } = useLayoutObserver(parent!)
const isTableLayoutAuto = parent?.props.tableLayout === 'auto'
const saveIndexSelection = new Map()
const theadRef = ref()
const updateFixedColumnStyle = () => {
setTimeout(() => {
if (saveIndexSelection.size > 0) {
saveIndexSelection.forEach((column, key) => {
const el = theadRef.value.querySelector(
`.${key.replace(/\s/g, '.')}`
)
if (el) {
const width = el.getBoundingClientRect().width
column.width = width
}
})
saveIndexSelection.clear()
}
})
}
onMounted(async () => {
// Need double await, because updateColumns is executed after nextTick for now
await nextTick()
await nextTick()
const { prop, order } = props.defaultSort
parent?.store.commit('sort', { prop, order, init: true })
updateFixedColumnStyle()
})
const {
handleHeaderClick,
handleHeaderContextMenu,
@ -118,6 +143,10 @@ export default defineComponent({
handleFilterClick,
isGroup,
toggleAllSelection,
saveIndexSelection,
isTableLayoutAuto,
theadRef,
updateFixedColumnStyle,
}
},
render() {
@ -137,11 +166,14 @@ export default defineComponent({
handleMouseOut,
store,
$parent,
saveIndexSelection,
isTableLayoutAuto,
} = this
let rowSpan = 1
return h(
'thead',
{
ref: 'theadRef',
class: { [ns.is('group')]: isGroup },
},
columnRows.map((subColumns, rowIndex) =>
@ -156,15 +188,19 @@ export default defineComponent({
if (column.rowSpan > rowSpan) {
rowSpan = column.rowSpan
}
const _class = getHeaderCellClass(
rowIndex,
cellIndex,
subColumns,
column
)
if (isTableLayoutAuto && column.fixed) {
saveIndexSelection.set(_class, column)
}
return h(
'th',
{
class: getHeaderCellClass(
rowIndex,
cellIndex,
subColumns,
column
),
class: _class,
colspan: column.colSpan,
key: `${column.id}-thead`,
rowspan: column.rowSpan,

View File

@ -1,6 +1,6 @@
// @ts-nocheck
import { createVNode, render } from 'vue'
import { flatMap, get } from 'lodash-unified'
import { flatMap, get, merge } from 'lodash-unified'
import {
hasOwn,
isArray,
@ -16,6 +16,7 @@ import ElTooltip, {
} from '@element-plus/components/tooltip'
import type { Table, TreeProps } from './table/defaults'
import type { TableColumnCtx } from './table-column/defaults'
import type { VNode } from 'vue'
export type TableOverflowTooltipOptions = Partial<
Pick<
@ -36,6 +37,7 @@ export type TableOverflowTooltipOptions = Partial<
type RemovePopperFn = (() => void) & {
trigger?: HTMLElement
vm?: VNode
}
export const getCell = function (event: Event) {
@ -363,6 +365,20 @@ export function walkTreeNode(
})
}
const getTableOverflowTooltipProps = (
props: TableOverflowTooltipOptions,
content: string
) => {
return {
content,
...props,
popperOptions: {
strategy: 'fixed',
...props.popperOptions,
},
}
}
export let removePopper: RemovePopperFn | null = null
export function createTablePopper(
@ -372,17 +388,16 @@ export function createTablePopper(
table: Table<[]>
) {
if (removePopper?.trigger === trigger) {
merge(
removePopper!.vm.component.props,
getTableOverflowTooltipProps(props, popperContent)
)
return
}
removePopper?.()
const parentNode = table?.refs.tableWrapper
const ns = parentNode?.dataset.prefix
const popperOptions = {
strategy: 'fixed',
...props.popperOptions,
}
const vm = createVNode(ElTooltip, {
content: popperContent,
virtualTriggering: true,
virtualRef: trigger,
appendTo: parentNode,
@ -390,11 +405,7 @@ export function createTablePopper(
transition: 'none', // Default does not require transition
offset: 0,
hideAfter: 0,
...props,
popperOptions,
onHide: () => {
removePopper?.()
},
...getTableOverflowTooltipProps(props, popperContent),
})
vm.appContext = { ...table.appContext, ...table }
const container = document.createElement('div')
@ -407,6 +418,7 @@ export function createTablePopper(
removePopper = null
}
removePopper.trigger = trigger
removePopper.vm = vm
scrollContainer?.addEventListener('scroll', removePopper)
}

View File

@ -202,38 +202,36 @@ const TabNav = defineComponent({
}
}
const changeTab = (e: KeyboardEvent) => {
const code = e.code
const changeTab = (event: KeyboardEvent) => {
let step = 0
const { up, down, left, right } = EVENT_CODE
if (![up, down, left, right].includes(code)) return
// 左右上下键更换tab
const tabList = Array.from(
(e.currentTarget as HTMLDivElement).querySelectorAll<HTMLDivElement>(
'[role=tab]:not(.is-disabled)'
)
)
const currentIndex = tabList.indexOf(e.target as HTMLDivElement)
let nextIndex: number
if (code === left || code === up) {
// left
if (currentIndex === 0) {
// first
nextIndex = tabList.length - 1
} else {
nextIndex = currentIndex - 1
}
} else {
// right
if (currentIndex < tabList.length - 1) {
// not last
nextIndex = currentIndex + 1
} else {
nextIndex = 0
}
switch (event.code) {
case EVENT_CODE.left:
case EVENT_CODE.up:
step = -1
break
case EVENT_CODE.right:
case EVENT_CODE.down:
step = 1
break
default:
return
}
const tabList = Array.from(
(
event.currentTarget as HTMLDivElement
).querySelectorAll<HTMLDivElement>('[role=tab]:not(.is-disabled)')
)
const currentIndex = tabList.indexOf(event.target as HTMLDivElement)
let nextIndex = currentIndex + step
if (nextIndex < 0) {
nextIndex = tabList.length - 1
} else if (nextIndex >= tabList.length) {
nextIndex = 0
}
tabList[nextIndex].focus({ preventScroll: true }) // 改变焦点元素
tabList[nextIndex].click() // 选中下一个tab
setFocus()

View File

@ -199,7 +199,8 @@ const Tabs = defineComponent({
tabindex="0"
onClick={handleTabAdd}
onKeydown={(ev: KeyboardEvent) => {
if (ev.code === EVENT_CODE.enter) handleTabAdd()
if ([EVENT_CODE.enter, EVENT_CODE.numpadEnter].includes(ev.code))
handleTabAdd()
}}
>
{addSlot ? (

View File

@ -78,7 +78,7 @@
</div>
</template>
<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, unref, watch } from 'vue'
import { computed, inject, nextTick, onMounted, ref, unref, watch } from 'vue'
import { debounce } from 'lodash-unified'
import { vRepeatClick } from '@element-plus/directives'
import ElScrollbar from '@element-plus/components/scrollbar'
@ -97,6 +97,8 @@ import type { TimeUnit } from '../constants'
import type { TimeList } from '../utils'
const props = defineProps(basicTimeSpinnerProps)
const pickerBase = inject('EP_PICKER_BASE') as any
const { isRange } = pickerBase.props
const emit = defineEmits(['change', 'select-range', 'set-option'])
const ns = useNamespace('time')
@ -135,10 +137,12 @@ const timePartials = computed<Record<TimeUnit, number>>(() => {
const timeList = computed(() => {
const { hours, minutes } = unref(timePartials)
const { role, spinnerDate } = props
const compare = !isRange ? spinnerDate : undefined
return {
hours: getHoursList(props.role),
minutes: getMinutesList(hours, props.role),
seconds: getSecondsList(hours, minutes, props.role),
hours: getHoursList(role, compare),
minutes: getMinutesList(hours, role, compare),
seconds: getSecondsList(hours, minutes, role, compare),
}
})

View File

@ -24,7 +24,7 @@ export const useTooltipTriggerProps = buildProps({
*/
triggerKeys: {
type: definePropType<string[]>(Array),
default: () => [EVENT_CODE.enter, EVENT_CODE.space],
default: () => [EVENT_CODE.enter, EVENT_CODE.numpadEnter, EVENT_CODE.space],
},
} as const)

View File

@ -99,7 +99,12 @@ export function useKeydown({ el$ }: UseKeydownOption, store: Ref<TreeStore>) {
const hasInput = currentItem.querySelector(
'[type="checkbox"]'
) as Nullable<HTMLInputElement>
if ([EVENT_CODE.enter, EVENT_CODE.space].includes(code) && hasInput) {
if (
[EVENT_CODE.enter, EVENT_CODE.numpadEnter, EVENT_CODE.space].includes(
code
) &&
hasInput
) {
ev.preventDefault()
hasInput.click()
}

View File

@ -4,9 +4,8 @@ import { EVENT_CODE } from '@element-plus/constants'
let registeredEscapeHandlers: ((e: KeyboardEvent) => void)[] = []
const cachedHandler = (e: Event) => {
const event = e as KeyboardEvent
if (event.key === EVENT_CODE.esc) {
const cachedHandler = (event: KeyboardEvent) => {
if (event.code === EVENT_CODE.esc) {
registeredEscapeHandlers.forEach((registeredHandler) =>
registeredHandler(event)
)