import React, { useEffect, useRef, useState } from "react";
import { useClickAway } from "react-use";
import styles from "./NavDropdown.module.scss";
import { createPortal } from "react-dom";

const NavDropdown = ({ items, children }) => {
    const [isOpened, setIsOpened] = useState(false);
    const [listStyle, setListStyle] = useState({});

    const buttonRef = useRef(null);
    const listRef = useRef(null);

    const handleDotsClick = (e) => {
        setIsOpened((prev) => !prev);
    };

    const updatePosition = () => {
        if (isOpened && buttonRef.current && listRef.current) {
            const buttonRect = buttonRef.current.getBoundingClientRect();
            const listRect = listRef.current.getBoundingClientRect();
            const viewportHeight = window.innerHeight;
            const viewportWidth = document.body.offsetWidth;

            let top = buttonRect.bottom;
            let left = buttonRect.left;

            if (top + listRect.height > viewportHeight) {
                top = buttonRect.top - listRect.height;
            }
            if (left + listRect.width > viewportWidth) {
                left = viewportWidth - listRect.width;
            }

            setListStyle({
                top: `${top + window.scrollY}px`,
                left: `${left + window.scrollX}px`,
            });
        }
    };

    useClickAway(listRef, () => {
        setIsOpened(false);
    });

    useEffect(() => {
        if (isOpened) {
            updatePosition();
        }
    }, [isOpened]);

    useEffect(() => {
        if (!isOpened) return;

        const handleResize = () => updatePosition();
        window.addEventListener("resize", handleResize);

        return () => {
            window.removeEventListener("resize", handleResize);
        };
    }, [isOpened]);

    return (
        <div className={styles["nav-dropdown"]}>
            {React.cloneElement(children, {
                ref: buttonRef,
                onClick: (e) => {
                    if (children.props.onClick) {
                        children.props.onClick(e);
                    }
                    handleDotsClick();
                },
            })}

            {isOpened &&
                createPortal(
                    <ul
                        ref={listRef}
                        className={styles["nav-dropdown__list"]}
                        style={{ ...listStyle }}
                    >
                        {items.map((item, index) => {
                            return (
                                <li key={index}>
                                    <a
                                        href="#"
                                        className={styles["nav-dropdown__link"]}
                                    >
                                        {item.text}
                                    </a>
                                </li>
                            );
                        })}
                    </ul>,
                    document.body
                )}
        </div>
    );
};

export default NavDropdown;
.nav-dropdown {
    &__list {
        position: absolute;
        top: -9999px;
        left: -9999px;
        background: #fff;
        border: 1px solid #f3f3f3;
        border-radius: 5px;
        box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
        padding: 10px 0;
        max-width: 100vw;
    }

    &__link {
        display: block;
        padding: 5px 10px;

        &:hover {
            background: #f3f3f3;
        }
    }
}
Рубрики: Без рубрики

0 комментариев

Добавить комментарий

Avatar placeholder

Ваш адрес email не будет опубликован. Обязательные поля помечены *