Перейти к содержимому

Dropdown & Scroll Link

/src/components/ui/Dropdown.jsx
import { useState, useEffect, useRef } from 'preact/hooks'
// Компонент для отображения выпадающего списка
const Dropdown = () => {
// Переменная состояния для отслеживания состояния открытия выпадающего списка
const [isOpen, setIsOpen] = useState(false)
// Ссылка на элемент выпадающего списка, позволяет отслеживать клики вне элемента
const dropdownRef = useRef(null)
// Константа для управления отступом при прокрутке
const scrollOffset = 80 // Отступ от верхней части окна
// Функция переключения видимости выпадающего списка
const toggleDropdown = () => {
setIsOpen(!isOpen)
}
// Функция прокрутки страницы до нужного элемента с учетом отступа
const handleScroll = (e, targetId, offset) => {
e.preventDefault() // Предотвращает стандартное поведение ссылки
const targetElement = document.getElementById(targetId) // Получение целевого элемента по ID
if (targetElement) {
const rect = targetElement.getBoundingClientRect() // Позиция элемента относительно окна
const scrollToPosition = window.scrollY + rect.top - offset // Вычисление позиции прокрутки
window.scrollTo({ top: scrollToPosition, behavior: 'smooth' }) // Плавная прокрутка
}
setIsOpen(false) // Закрытие выпадающего списка после прокрутки
}
// Использование эффекта для добавления и удаления слушателя событий
useEffect(() => {
// Закрытие выпадающего списка при клике вне его области
const handleClickOutside = (event) => {
// Проверка, если клик был вне элемента
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false) // Закрыть выпадающий список
}
}
// Добавление слушателя для отслеживания кликов по документу
document.addEventListener('mousedown', handleClickOutside)
// Удаление слушателя при размонтировании компонента
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, []) // Пустой массив зависимостей, эффект выполняется только при монтировании и размонтировании
return (
<div
className="relative inline-block text-left"
ref={dropdownRef}>
<button
type="button"
className="inline-flex w-full cursor-pointer justify-center rounded-full border border-slate-200 bg-white p-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none"
aria-label="Открыть"
onClick={toggleDropdown}>
<svg
className="hs-collapse-open:hidden shrink-0 size-3.5"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<line
x1="3"
x2="21"
y1="6"
y2="6"></line>
<line
x1="3"
x2="21"
y1="12"
y2="12"></line>
<line
x1="3"
x2="21"
y1="18"
y2="18"></line>
</svg>
</button>
{isOpen && (
<ul className="ring-opacity-5 absolute right-0 mt-2 w-36 origin-top-right rounded-md bg-white shadow-lg shadow-blue-600/15 focus:outline-none">
<li>
<a
href="#about"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-slate-50"
onClick={(e) => handleScroll(e, 'about', scrollOffset)}>
About
</a>
</li>
<li>
<a
href="#projects"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-slate-50"
onClick={(e) => handleScroll(e, 'projects', scrollOffset)}>
Projects
</a>
</li>
</ul>
)}
</div>
)
}
export default Dropdown