From 43173c6f762ea7fa48bd7c21395ae00a3e8f0244 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 09:33:54 +0000 Subject: [PATCH 1/5] Add navigation menu component from Base UI - Implement NavigationMenuBaseComponent extending BaseUIComponent - Add all navigation menu sub-components following existing patterns - Create HighLevelNavigationMenu wrapper for simple usage - Add to lazy loading system in __init__.py - Include example in demo application - Component renders and functions correctly with clickable buttons Co-Authored-By: Carlos Cutillas --- demo/demo/demo.py | 7 + reflex_ui/__init__.py | 1 + reflex_ui/components/base/navigation_menu.py | 411 +++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 reflex_ui/components/base/navigation_menu.py diff --git a/demo/demo/demo.py b/demo/demo/demo.py index d884642..15e1df0 100644 --- a/demo/demo/demo.py +++ b/demo/demo/demo.py @@ -58,6 +58,13 @@ def index() -> rx.Component: on_value_change=lambda value: rx.toast.success(f"Value: {value}"), on_open_change=lambda value: rx.toast.success(f"Open: {value}"), ), + ui.navigation_menu( + items=[ + ("Home", lambda: rx.toast.success("Home clicked")), + ("About", lambda: rx.toast.success("About clicked")), + ("Contact", lambda: rx.toast.success("Contact clicked")), + ], + ), ui.theme_switcher(class_name="absolute top-4 right-4"), class_name=ui.cn( "flex flex-col gap-6 items-center justify-center h-screen", "bg-secondary-1" diff --git a/reflex_ui/__init__.py b/reflex_ui/__init__.py index 446e414..68a6459 100644 --- a/reflex_ui/__init__.py +++ b/reflex_ui/__init__.py @@ -13,6 +13,7 @@ "components.base.input": ["input"], "components.base.link": ["link"], "components.base.menu": ["menu"], + "components.base.navigation_menu": ["navigation_menu"], "components.base.popover": ["popover"], "components.base.scroll_area": ["scroll_area"], "components.base.select": ["select"], diff --git a/reflex_ui/components/base/navigation_menu.py b/reflex_ui/components/base/navigation_menu.py new file mode 100644 index 0000000..b63d4aa --- /dev/null +++ b/reflex_ui/components/base/navigation_menu.py @@ -0,0 +1,411 @@ +"""Custom navigation menu component.""" + +from typing import Literal + +from reflex.components.component import Component, ComponentNamespace +from reflex.components.core.foreach import foreach +from reflex.event import EventHandler, passthrough_event_spec +from reflex.utils.imports import ImportVar +from reflex.vars.base import Var + +from reflex_ui.components.base.button import button +from reflex_ui.components.base_ui import PACKAGE_NAME, BaseUIComponent +from reflex_ui.utils.twmerge import cn + +LiteralNavigationMenuOrientation = Literal["horizontal", "vertical"] +LiteralSide = Literal["top", "right", "bottom", "left"] +LiteralAlign = Literal["start", "center", "end"] +LiteralPositionMethod = Literal["absolute", "fixed"] +LiteralCollisionAvoidance = Literal["flip", "shift", "auto"] + + +class ClassNames: + """Class names for navigation menu components.""" + + ROOT = "relative" + LIST = "flex items-center gap-1" + ITEM = "relative" + TRIGGER = "flex items-center gap-1 px-3 py-2 text-sm font-medium rounded-md hover:bg-secondary-3 focus:outline-none focus-visible:ring-1 focus-visible:ring-primary-4 cursor-pointer select-none" + CONTENT = "absolute top-full left-0 mt-1 min-w-48 origin-top-left border border-secondary-a4 bg-secondary-1 shadow-large rounded-md p-1 z-50 transition-[transform,scale,opacity] data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" + LINK = "block px-3 py-2 text-sm text-secondary-12 hover:bg-secondary-3 rounded-sm cursor-pointer select-none outline-none focus:bg-secondary-3" + ICON = "size-4 text-secondary-10" + PORTAL = "relative" + POSITIONER = "outline-none" + POPUP = "outline-none" + VIEWPORT = "relative overflow-hidden" + ARROW = "data-[side=bottom]:top-[-8px] data-[side=left]:right-[-13px] data-[side=left]:rotate-90 data-[side=right]:left-[-13px] data-[side=right]:-rotate-90 data-[side=top]:bottom-[-8px] data-[side=top]:rotate-180" + BACKDROP = "fixed inset-0 z-40" + + +class NavigationMenuBaseComponent(BaseUIComponent): + """Base component for navigation menu components.""" + + library = f"{PACKAGE_NAME}/navigation-menu" + + @property + def import_var(self): + """Return the import variable for the navigation menu component.""" + return ImportVar(tag="NavigationMenu", package_path="", install=False) + + +class NavigationMenuRoot(NavigationMenuBaseComponent): + """Groups all parts of the navigation menu. Renders a