pref: improve hover and pre-selection state for formkit select (#6607)

#### What type of PR is this?

/kind improvement
/area ui
/milestone 2.20.x

#### What this PR does / why we need it:

优化了 Formkit 中的 selec 组件,使得其使用快捷键与鼠标选中的预选状态始终只有一个。且在多选状态下按回车时,不再预选跳转至第一个选项。

#### How to test it?

在多选状态下测试使用键盘进行预选及使用鼠标 hover 进行预选的状态是否符合预期。

#### Which issue(s) this PR fixes:

Fixes #6600

#### Does this PR introduce a user-facing change?
```release-note
优化 Formkit select 组件在多选状态下的待选中状态。
```
pull/6651/head
Takagi 2024-09-13 10:22:26 +08:00 committed by GitHub
parent 7ed859cefb
commit 25eec1ec4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 51 additions and 3 deletions

View File

@ -27,6 +27,8 @@ const emit = defineEmits<{
const selectedIndex = ref<number>(0);
const selectOptionRef = ref<HTMLElement>();
const oldEvent = ref<MouseEvent | undefined>();
const isCursorHidden = ref(false);
const selectedValues = computed(() =>
props.selectedOptions?.map((option) => option.value)
@ -46,6 +48,9 @@ const getSelectedIndex = () => {
};
const handleKeydown = (event: KeyboardEvent) => {
if (selectOptionRef.value) {
isCursorHidden.value = true;
}
const key = event.key;
if (key === "ArrowUp") {
selectedIndex.value =
@ -138,7 +143,7 @@ const handleOptionScroll = (state: UseScrollReturn) => {
};
watch(
() => props.options,
props.options,
() => {
selectedIndex.value = getSelectedIndex();
},
@ -153,6 +158,48 @@ watch(
handleScrollIntoView();
}
);
/**
* check if cursor is changed
*
* @param newEvent
* @param oldEvent
*/
const isCursorChanged = (
newEvent: MouseEvent,
oldEvent: MouseEvent | undefined
) => {
if (!oldEvent) {
return true;
}
return (
newEvent.screenX !== oldEvent.screenX ||
newEvent.screenY !== oldEvent.screenY
);
};
const handleMouseover = (event: MouseEvent) => {
if (isCursorHidden.value) {
if (!oldEvent.value) {
oldEvent.value = event;
}
if (isCursorChanged(event, oldEvent.value)) {
isCursorHidden.value = false;
oldEvent.value = undefined;
}
return;
}
const target = event.target as HTMLElement;
if (
target.classList.contains("select-option-item") &&
target instanceof HTMLElement
) {
const parentElement = target.parentElement as HTMLElement;
const index = Array.from(parentElement.children).indexOf(target);
selectedIndex.value = index;
}
};
</script>
<template>
@ -160,17 +207,18 @@ watch(
id="select-option"
ref="selectOptionRef"
v-scroll="[handleOptionScroll, { throttle: 10 }]"
class="select max-h-64 cursor-pointer overflow-y-auto p-1.5"
class="select max-h-64 overflow-y-auto p-1.5"
:class="[isCursorHidden ? 'cursor-none' : 'cursor-pointer']"
role="list"
tabindex="-1"
@keydown="handleKeydown"
@mouseover="handleMouseover"
>
<template v-for="(option, index) in options" :key="option.value">
<SelectOptionItem
class="select-option-item"
:option="option"
:class="{
'hover:bg-zinc-100': !isDisabled(option),
'bg-zinc-100': !isDisabled(option) && selectedIndex === index,
'selected !bg-zinc-200/60':
selectedValues && selectedValues.includes(option.value),