可用于在 Vue 应用程序中创建工具提示、弹出框和菜单的最流行的库之一是floating-vue。虽然floating-vue提供了一个Dropdown组件,但它的样式和添加其他功能(例如键盘导航)由用户决定。

我们将创建什么

我们将使用floating-vueDropdown组件作为基础,然后在顶部添加键盘功能。然后,我们将使用该组件使用 Tailwind 创建一个下拉菜单。

最终结果将类似于Bootstrap 的下拉组件。

图片说明

让我们开始吧!

下拉组件

幸运的是,Vue.js 允许我们使用“包装组件”技术轻松扩展任何组件。所以我们的最终组件将包括floating-vueDropdown 的所有功能以及:

  • 能够使用光标上/下键在单个下拉项目中移动

  • 用于键盘导航的下拉项目的可定制 CSS 选择器

  • 当下拉触发器有焦点时,用SpaceEnter键打开和关闭下拉

  • 使用键盘导航菜单时防止页面滚动

  • 单击外部或使用Esc键关闭下拉菜单

zoz100037

<template>
  <FloatingVueDropdown ref="popoverRef" :distance="14" :triggers="['click']" theme="dropdown-menu" placement="bottom-start" auto-hide @show="onShow" @hide="onHide">
    <template v-for="(_, slot) in $slots" #[slot]="scope">
      <slot :name="slot" v-bind="scope || {}" />
    </template>
  </FloatingVueDropdown>
</template>

<script setup>
import { Dropdown as FloatingVueDropdown } from 'floating-vue';
import { onBeforeUnmount, ref } from 'vue';

const popoverRef = ref(null);

const props = defineProps({
  itemSelector: {
    type: String,
    default: 'li > a:not(:disabled)',
  },
});

const popoverKeydown = (e) => {
  const popover = popoverRef.value;

  if (!popover) {
    return;
  }

  if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
    e.preventDefault();

    const items = [...popover.$refs.popperContent.$el.querySelectorAll(props.itemSelector)];

    if (!items.length) {
      return;
    }

    let index = items.indexOf(e.target);
    index = e.key === 'ArrowDown' ? index + 1 : index - 1;
    items[index]?.focus();
  }

  if ((e.key === 'Enter' || e.key === ' ') && e.target === popover.$refs.popperContent.$el) {
    e.preventDefault();
    popover.hide();
  }
};

const onShow = () => {
  document.addEventListener('keydown', popoverKeydown);
};

const onHide = () => {
  document.removeEventListener('keydown', popoverKeydown);
  const popover = popoverRef.value;
  popover?.$refs.popper.$_targetNodes[0].focus();
};

onBeforeUnmount(() => {
  document.removeEventListener('keydown', popoverKeydown);
});
</script>

<style>
.v-popper--theme-dropdown-menu .v-popper__arrow-container {
  display: none;
}
</style>

进入全屏模式 退出全屏模式

最重要部件的分解

  • 将槽传递给floating-vue下拉组件。
<FloatingVueDropdown>
  <template v-for="(_, slot) in $slots" #[slot]="scope">
    <slot :name="slot" v-bind="scope || {}" />
  </template>
</FloatingVueDropdown>

进入全屏模式 退出全屏模式

  • 设置一些组件默认值

*ref="popoverRef"- 弹出框ref,我们需要它来进行与 DOM 相关的操作。

*:distance="14"- 设置下拉触发器和下拉菜单之间的距离

*:triggers="['click']"- 点击打开下拉菜单

*theme="dropdown-menu"- 将 a 主题设置为我们自己的主题,基本上删除所有默认的floating-vue``Dropdown样式。

*placement="bottom-start"- 下拉菜单的默认位置

*auto-hide @show="onShow"- 显示Dropdown时运行的方法

*@hide="onHide"- 当Dropdown被隐藏时运行的方法

  • itemSelector属性 - 这是组件将在内部使用的选择器,以获取键盘导航将循环通过的下拉项目。

  • popoverKeydown方法 - 此方法在显示下拉列表时注册为document``keydown侦听器(在onShow方法中),包括所有键盘功能。我们正在侦听向上/向下箭头按键,并根据方向在一个项目上设置focus的下拉项目的Array循环。我们还监听EnterSpace按键,所以当这些发生在下拉触发器上时,我们打开下拉菜单。注意e.preventDefault();的用法,这是为了在打开下拉菜单时使用向上/向下键时页面不会滚动。

  • onHide方法移除document``keydown监听器并将焦点返回到下拉触发器。

结构和样式

我们已经准备好了基础Dropdown组件,让我们用它来使用 Tailwind 创建一个下拉菜单!

App.vue

<template>
  <Dropdown class="inline" popper-class="w-64 bg-white border rounded-lg shadow-md">
    <!-- Dropdown trigger -->
    <button
      class="inline-block px-6 py-3 bg-blue-600 text-white leading-tight rounded hover:bg-blue-700 focus:bg-blue-700 focus:outline-none focus:ring-0 active:bg-blue-800 transition duration-150 ease-in-out"
    >
      Dropdown button
    </button>

    <!-- Dropdown content -->
    <template #popper="{ hide }">
      <ul class="py-1 text-gray-70">
        <li>
          <a href="#" class="block py-2 px-4 hover:bg-gray-100 focus:bg-gray-100 outline-none" @click="hide">
            Click me to close
          </a>
        </li>
        <li>
          <a href="#" class="block py-2 px-4 hover:bg-gray-100 focus:bg-gray-100 outline-none">Menu item</a>
        </li>
        <li>
          <a href="#" class="block py-2 px-4 hover:bg-gray-100 focus:bg-gray-100 outline-none">Another menu item</a>
        </li>
      </ul>
    </template>
  </Dropdown>
</template>

<script setup>
import Dropdown from './Dropdown.vue'
</script>

进入全屏模式 退出全屏模式

而已!

这是Stackblitz 的最终结果的实时示例,这里是。

您可能还想查看vue-simple-dropdown,这是一个floating-vue的下拉组件,可以导入到您的 Vue 项目中!

Logo

前往低代码交流专区

更多推荐