fix: dropdown options cannot be clicked in some mobile devices (#4116)

#### What type of PR is this?

/area console
/kind improvement
/milestone 2.7.x

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

修复在部分移动端浏览器(比如 iOS Safari)中,下拉框组件(VDropdown)的选项点击无效的问题,即没有触发 click 事件。此问题的原因可能是因为我们用的 floating-vue 组件提供的 `v-close-popper` 指令的兼容问题,最小复现:https://stackblitz.com/edit/vitejs-vite-ncpzhj?file=src%2FApp.vue

此 PR 改写了关闭下拉框的方式,不再使用 v-close-popper 指令,而且对其他使用此组件的地方没有破坏性更新。

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

Fixes https://github.com/halo-dev/halo/issues/3689
Ref https://github.com/halo-dev/halo/issues/2699

#### Special notes for your reviewer:

如果有条件可以在移动端测试一下,尤其是 iOS Safari,目前在桌面端 Chrome 的设备模拟中测试正常。

#### Does this PR introduce a user-facing change?

```release-note
修复 Console 端的下拉框组件选项在移动端无法正常点击的问题。
```
pull/4061/head
Ryan Wang 2023-06-26 21:21:57 +08:00 committed by GitHub
parent aaa3548c97
commit 27ef8d3bab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 57 additions and 30 deletions

View File

@ -1,6 +1,8 @@
<script lang="ts" setup>
import { Dropdown as FloatingDropdown, type Placement } from "floating-vue";
import "floating-vue/dist/style.css";
import { provide, ref } from "vue";
import { DropdownContextInjectionKey } from "./symbols";
withDefaults(
defineProps<{
@ -15,13 +17,28 @@ withDefaults(
}
);
const dropdownRef = ref();
function hide() {
dropdownRef.value?.hide();
}
provide(DropdownContextInjectionKey, {
hide,
});
const emit = defineEmits<{
(event: "show"): void;
}>();
defineExpose({
hide,
});
</script>
<template>
<FloatingDropdown
ref="dropdownRef"
:placement="placement"
:triggers="triggers"
@show="emit('show')"

View File

@ -1,5 +1,6 @@
<script lang="ts" setup>
import { VClosePopper } from "floating-vue";
import { DropdownContextInjectionKey } from "./symbols";
import { inject } from "vue";
withDefaults(
defineProps<{
@ -11,15 +12,26 @@ withDefaults(
type: "default",
}
);
const emit = defineEmits<{
(event: "click", e: MouseEvent): void;
}>();
const { hide } = inject(DropdownContextInjectionKey) || {};
function onClick(e: MouseEvent) {
hide?.();
emit("click", e);
}
</script>
<template>
<div
v-close-popper
class="dropdown-item-wrapper"
:class="[`dropdown-item-wrapper--${type}${selected ? '--selected' : ''}`]"
role="menuitem"
tabindex="-1"
@click="onClick"
>
<div class="flex items-center gap-3">
<slot name="prefix-icon" />

View File

@ -2,3 +2,4 @@ export { default as VDropdown } from "./Dropdown.vue";
export { default as VDropdownItem } from "./DropdownItem.vue";
export { default as VDropdownDivider } from "./DropdownDivider.vue";
export { VClosePopper } from "floating-vue";
export * from "./symbols";

View File

@ -0,0 +1,5 @@
import type { InjectionKey } from "vue";
export const DropdownContextInjectionKey: InjectionKey<{
hide: () => void;
}> = Symbol("dropdown-context");

View File

@ -22,6 +22,8 @@ const emit = defineEmits<{
const { categories } = usePostCategory();
const dropdown = ref();
const handleSelect = (category: Category) => {
if (
props.selected &&
@ -34,6 +36,8 @@ const handleSelect = (category: Category) => {
emit("update:selected", category);
emit("select", category);
dropdown.value.hide();
};
function onDropdownShow() {
@ -71,7 +75,7 @@ const searchResults = computed(() => {
</script>
<template>
<VDropdown :classes="['!p-0']" @show="onDropdownShow">
<VDropdown ref="dropdown" :classes="['!p-0']" @show="onDropdownShow">
<slot />
<template #popper>
<div class="h-96 w-80">
@ -91,7 +95,6 @@ const searchResults = computed(() => {
<li
v-for="(category, index) in searchResults"
:key="index"
v-close-popper
@click="handleSelect(category)"
>
<VEntity

View File

@ -23,6 +23,8 @@ const emit = defineEmits<{
const { tags } = usePostTag();
const dropdown = ref();
const handleSelect = (tag: Tag) => {
if (props.selected && tag.metadata.name === props.selected.metadata.name) {
emit("update:selected", undefined);
@ -32,6 +34,8 @@ const handleSelect = (tag: Tag) => {
emit("update:selected", tag);
emit("select", tag);
dropdown.value.hide();
};
function onDropdownShow() {
@ -69,7 +73,7 @@ const searchResults = computed(() => {
</script>
<template>
<VDropdown :classes="['!p-0']" @show="onDropdownShow">
<VDropdown ref="dropdown" :classes="['!p-0']" @show="onDropdownShow">
<slot />
<template #popper>
<div class="h-96 w-80">
@ -89,7 +93,6 @@ const searchResults = computed(() => {
<li
v-for="(tag, index) in searchResults"
:key="index"
v-close-popper
@click="handleSelect(tag)"
>
<VEntity

View File

@ -27,6 +27,8 @@ const emit = defineEmits<{
const { users, handleFetchUsers } = useUserFetch();
const dropdown = ref();
const handleSelect = (user: User) => {
if (props.selected && user.metadata.name === props.selected.metadata.name) {
emit("update:selected", undefined);
@ -36,6 +38,8 @@ const handleSelect = (user: User) => {
emit("update:selected", user);
emit("select", user);
dropdown.value.hide();
};
function onDropdownShow() {
@ -71,7 +75,7 @@ const searchResults = computed(() => {
</script>
<template>
<VDropdown :classes="['!p-0']" @show="onDropdownShow">
<VDropdown ref="dropdown" :classes="['!p-0']" @show="onDropdownShow">
<slot />
<template #popper>
<div class="h-96 w-80">
@ -91,7 +95,6 @@ const searchResults = computed(() => {
<li
v-for="(user, index) in searchResults"
:key="index"
v-close-popper
@click="handleSelect(user)"
>
<VEntity

View File

@ -266,15 +266,10 @@ onMounted(async () => {
{{ $t("core.common.buttons.delete") }}
</VDropdownItem>
<template #popper>
<VDropdownItem
v-close-popper.all
type="danger"
@click="handleDelete(group)"
>
<VDropdownItem type="danger" @click="handleDelete(group)">
{{ $t("core.attachment.group_list.operations.delete.button") }}
</VDropdownItem>
<VDropdownItem
v-close-popper.all
type="danger"
@click="handleDeleteWithAttachments(group)"
>

View File

@ -208,18 +208,10 @@ const handleUninstall = async (theme: Theme, deleteExtensions?: boolean) => {
{{ $t("core.common.buttons.uninstall") }}
</VDropdownItem>
<template #popper>
<VDropdownItem
v-close-popper.all
type="danger"
@click="handleUninstall(theme)"
>
<VDropdownItem type="danger" @click="handleUninstall(theme)">
{{ $t("core.common.buttons.uninstall") }}
</VDropdownItem>
<VDropdownItem
v-close-popper.all
type="danger"
@click="handleUninstall(theme, true)"
>
<VDropdownItem type="danger" @click="handleUninstall(theme, true)">
{{ $t("core.theme.operations.uninstall_and_delete_config.button") }}
</VDropdownItem>
</template>

View File

@ -154,14 +154,10 @@ const handleResetSettingConfig = async () => {
{{ $t("core.common.buttons.uninstall") }}
</VDropdownItem>
<template #popper>
<VDropdownItem v-close-popper.all type="danger" @click="uninstall">
<VDropdownItem type="danger" @click="uninstall">
{{ $t("core.common.buttons.uninstall") }}
</VDropdownItem>
<VDropdownItem
v-close-popper.all
type="danger"
@click="uninstall(true)"
>
<VDropdownItem type="danger" @click="uninstall(true)">
{{ $t("core.plugin.list.actions.uninstall_and_delete_config") }}
</VDropdownItem>
</template>