mirror of https://github.com/halo-dev/halo
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
parent
7ed859cefb
commit
25eec1ec4f
|
@ -27,6 +27,8 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const selectedIndex = ref<number>(0);
|
const selectedIndex = ref<number>(0);
|
||||||
const selectOptionRef = ref<HTMLElement>();
|
const selectOptionRef = ref<HTMLElement>();
|
||||||
|
const oldEvent = ref<MouseEvent | undefined>();
|
||||||
|
const isCursorHidden = ref(false);
|
||||||
|
|
||||||
const selectedValues = computed(() =>
|
const selectedValues = computed(() =>
|
||||||
props.selectedOptions?.map((option) => option.value)
|
props.selectedOptions?.map((option) => option.value)
|
||||||
|
@ -46,6 +48,9 @@ const getSelectedIndex = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeydown = (event: KeyboardEvent) => {
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (selectOptionRef.value) {
|
||||||
|
isCursorHidden.value = true;
|
||||||
|
}
|
||||||
const key = event.key;
|
const key = event.key;
|
||||||
if (key === "ArrowUp") {
|
if (key === "ArrowUp") {
|
||||||
selectedIndex.value =
|
selectedIndex.value =
|
||||||
|
@ -138,7 +143,7 @@ const handleOptionScroll = (state: UseScrollReturn) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.options,
|
props.options,
|
||||||
() => {
|
() => {
|
||||||
selectedIndex.value = getSelectedIndex();
|
selectedIndex.value = getSelectedIndex();
|
||||||
},
|
},
|
||||||
|
@ -153,6 +158,48 @@ watch(
|
||||||
handleScrollIntoView();
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -160,17 +207,18 @@ watch(
|
||||||
id="select-option"
|
id="select-option"
|
||||||
ref="selectOptionRef"
|
ref="selectOptionRef"
|
||||||
v-scroll="[handleOptionScroll, { throttle: 10 }]"
|
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"
|
role="list"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
@keydown="handleKeydown"
|
@keydown="handleKeydown"
|
||||||
|
@mouseover="handleMouseover"
|
||||||
>
|
>
|
||||||
<template v-for="(option, index) in options" :key="option.value">
|
<template v-for="(option, index) in options" :key="option.value">
|
||||||
<SelectOptionItem
|
<SelectOptionItem
|
||||||
class="select-option-item"
|
class="select-option-item"
|
||||||
:option="option"
|
:option="option"
|
||||||
:class="{
|
:class="{
|
||||||
'hover:bg-zinc-100': !isDisabled(option),
|
|
||||||
'bg-zinc-100': !isDisabled(option) && selectedIndex === index,
|
'bg-zinc-100': !isDisabled(option) && selectedIndex === index,
|
||||||
'selected !bg-zinc-200/60':
|
'selected !bg-zinc-200/60':
|
||||||
selectedValues && selectedValues.includes(option.value),
|
selectedValues && selectedValues.includes(option.value),
|
||||||
|
|
Loading…
Reference in New Issue