Skip to content

A useful headless component (hook) that gives you all the functions you need to create a multi-level menu using your own components!

License

Notifications You must be signed in to change notification settings

cdes/react-headless-nested-menu

Repository files navigation

React Headless Nested Menu Logo

React Headless Nested Menu

A useful headless component (hook) that gives you all the functions you need to create a multi-level menu using your own components!

Features

  • Only functionality, no need to fight with CSS classes and overrides to customize your menu.
  • Created in TypeScript, so you get types out of the box.
  • Fully configurable behavior (open on click or hover).

React Headless Nested Menu Logo

Installation

yarn add react-headless-nested-menu

Usage

You can import the generated bundle to use the whole library generated by this starter:

import React from "react";
import { useNestedMenu } from "react-headless-nested-menu";

function App() {
  const {
    getToggleButtonProps,
    getMenuProps,
    getItemProps,
    getOpenTriggerProps,
    getCloseTriggerProps,
    getMenuOffsetStyles,
    isOpen,
    isSubMenuOpen,
    toggleMenu
  } = useNestedMenu({
    items
  });

  const [item, setItem] = useState<MenuItem>();

  // your custom function to render items
  const renderItem = (item: MenuItem) => (
    <div
      {...getItemProps(item)}
      className="relative my-1 first:mt-0 last:mb-0"
      {...getOpenTriggerProps("onPointerEnter", item)}
      onClick={(event) => {
        event.stopPropagation();
        setItem(item);
        toggleMenu();
      }}
    >
      <div
        className={classnames(
          "flex flex-row justify-between items-center rounded-lg flex-1 h-8 flex items-center px-2",
          {
            "text-gray-600 hover:text-gray-800 hover:bg-gray-200": !isSubMenuOpen(
              item
            ),
            "text-gray-800 bg-gray-200": isSubMenuOpen(item)
          }
        )}
      >
        {item.label}
        {item.subMenu && <Chevron />}
      </div>

      {/* Only show submenu when there's a submenu & it's open */}
      {item.subMenu && isSubMenuOpen(item) && renderMenu(item.subMenu, item)}
    </div>
  );

  // your custom function to render menus (root menu & sub-menus)
  const renderMenu = (items: Items, parentItem?: MenuItem) => (
    <div
      {...getMenuProps(parentItem)}
      style={{
        position: "absolute",
        ...getMenuOffsetStyles(parentItem)
      }}
      className={classnames(
        "bg-white p-2 shadow-lg rounded-lg select-none border border-gray-100 relative z-10",
        {
          "ms-2": typeof parentItem === "undefined", //for root menu
          "-mt-3": typeof parentItem !== "undefined" //for submenus only
        }
      )}
      {...getCloseTriggerProps("onPointerLeave", parentItem)}
    >
      <div>{items.map((item) => renderItem(item))}</div>

      {/* add hit area */}
      {parentItem && (
        <div
          style={{
            position: "absolute",
            top: -8,
            bottom: -8,
            left: -8,
            right: -8,
            zIndex: -1
          }}
        ></div>
      )}
    </div>
  );

  return (
    <div className="w-64 p-4 rounded-lg flex flex-col ms-4 mt-4">
      <button
        className="text-gray-600 border-2 border-gray-600 rounded-lg h-10 focus:outline-none"
        {...getToggleButtonProps()}
      >
        {item ? item.label : "Open Menu"}
      </button>
      {isOpen && renderMenu(items)}
    </div>
  );
}

const rootElement = document.getElementById("root");
React.render(<App />, rootElement);

To do

  • Improve documentation.
  • Add more example.
  • Add tests.
  • Use popper for positioning menus.

Examples

Credits

About

A useful headless component (hook) that gives you all the functions you need to create a multi-level menu using your own components!

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy