Выпадающее меню Vue с помощью Floating Vue и Tailwind

Одной из самых популярных библиотек для создания всплывающих подсказок, всплывающих окон и меню в приложениях Vue является floating-vue. Хотя floating-vue предоставляет компонент Dropdown, его стилизация и добавление дополнительной функциональности, например, клавиатурной навигации, остается на усмотрение пользователя.

Что мы создадим

Мы будем использовать компонент Dropdown из floating-vue в качестве основы, а затем добавим функциональность клавиатуры. Затем мы будем использовать этот компонент для создания выпадающего меню с помощью Tailwind.

Конечный результат будет напоминать компонент Dropdown из Bootstrap.

Давайте же приступим!

Компонент выпадающего меню

К счастью, Vue.js позволяет нам легко расширить любой компонент, используя технику «компонента-обертки». Так что наш конечный компонент будет включать в себя всю функциональность floating-vue Dropdown плюс:

  • Возможность перемещения по отдельным элементам выпадающего списка с помощью клавиш курсора вверх/вниз
  • Настраиваемый CSS селектор для выпадающих элементов, используемых для клавиатурной навигации
  • Открытие и закрытие выпадающего элемента с помощью клавиш Space и Enter, когда триггер выпадающего элемента имеет фокус
  • Предотвращение прокрутки страницы при навигации по меню с помощью клавиатуры
  • Закрыть выпадающее меню щелчком мыши за пределами или клавишей Esc.

Dropdown.vue

<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 Dropdown.
<FloatingVueDropdown>
  <template v-for="(_, slot) in $slots" #[slot]="scope">
    <slot :name="slot" v-bind="scope || {}" />
  </template>
</FloatingVueDropdown>
Вход в полноэкранный режим Выход из полноэкранного режима
  • Установите некоторые параметры компонента по умолчанию

  • Свойство itemSelector — это селектор, который компонент будет использовать внутри для получения выпадающих элементов, которые будут перебираться с помощью клавиатуры.

  • Метод popoverKeydown — Этот метод, который регистрируется как слушатель document keydown при отображении выпадающего списка (в методе onShow), включает всю функциональность клавиатуры. Мы прослушиваем нажатия клавиш стрелок вверх/вниз и циклически просматриваем Array выпадающих элементов, устанавливая focus на элемент в зависимости от направления. Мы также слушаем нажатия клавиш Enter и Space, и когда они происходят на триггере выпадающего элемента, мы открываем выпадающий элемент. Обратите внимание на использование 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!

Оцените статью
devanswers.ru
Добавить комментарий