# NavBuddy [![Hex version badge](https://img.shields.io/hexpm/v/navbuddy.svg)](https://hex.pm/packages/navbuddy) [![Hex docs badge](https://img.shields.io/badge/hex-docs-green.svg)](https://hexdocs.pm/navbuddy) [![License badge](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/SangRJ/navbuddy/blob/main/LICENSE) **Advanced Configurable Navigation Component for Phoenix LiveView** NavBuddy provides comprehensive navigation components for Phoenix LiveView applications, featuring dropdowns, mega menus, mobile-responsive design, native DaisyUI integration, and full accessibility support. ## Features - 🎯 **Advanced Menu System**: Single-layer dropdowns, multi-layer dropdowns, and mega menus - 📱 **Mobile-Responsive**: Drawer, collapse, and overlay modes for mobile devices - 🎨 **Native DaisyUI Integration**: True Tailwind CSS + DaisyUI support with theme compatibility - 🎨 **Standalone DaisyUI**: Built-in compatibility with DaisyUI components and themes - ♿ **Accessibility First**: WCAG 2.1 AA compliant with screen reader support - ⌨️ **Keyboard Navigation**: Full arrow key navigation with focus management - 🎨 **Modern Theming**: DaisyUI theme tokens with light/dark/auto theme support - 🔧 **Highly Configurable**: Extensive configuration options - 🚀 **Phoenix 1.8+ Ready**: Enhanced for Phoenix 1.8+ and LiveView 1.1+ - 📊 **Analytics Ready**: Built-in event tracking support ## Quick Start Add to your `mix.exs`: ```elixir def deps do [ {:navbuddy, "~> 0.4.0"} ] end ``` ## Installation & Setup Choose your integration approach: ### 🚀 Native DaisyUI Integration (Recommended for Phoenix 1.8+) For projects with Tailwind CSS + DaisyUI already set up: ```elixir # Add to mix.exs {:navbuddy, "~> 0.4.0"} ``` Then use native components: ```heex ``` **📖 [Complete Native Setup Guide](NATIVE_DAISYUI_GUIDE.md)** ### With DaisyUI (Recommended) If you're using DaisyUI in your Phoenix project, NavBuddy will automatically integrate with your existing themes and styling: ```html ``` ### Standalone (Without DaisyUI) Include the CSS and JavaScript in your layout: ```html ``` Use in your LiveView: ```elixir defmodule MyAppWeb.PageLive do use MyAppWeb, :live_view import NavBuddy.Component def mount(_params, _session, socket) do socket = assign(socket, :navigation_items, navigation_items()) {:ok, socket} end def render(assigns) do ~H""" <.nav_menu items={@navigation_items} current_path={@current_path} /> """ end defp navigation_items do [ %{id: "home", label: "Home", navigate: "/"}, %{id: "about", label: "About", navigate: "/about"}, %{ id: "products", label: "Products", navigate: "/products", dropdown: [ %{id: "web", label: "Web Apps", navigate: "/products/web"}, %{id: "mobile", label: "Mobile Apps", navigate: "/products/mobile"} ] } ] end end ``` ## Navigation Structure NavBuddy supports various navigation structures with comprehensive validation and helpful error messages. ### Required vs Optional Fields **Required fields:** - `id` (string) - Unique identifier for the navigation item - `label` (string) - Display text for the navigation item **Optional fields:** - `navigate` (string) - Path to navigate to (required for clickable items) - `icon` (string) - Icon class or identifier - `dropdown` (list) - List of child navigation items - `mega_menu` (map) - Mega menu configuration - `active` (boolean) - Whether the item is currently active - `disabled` (boolean) - Whether the item is disabled - `permissions` (string | list) - Required permissions to see the item ### Convenience Functions NavBuddy provides helper functions to create navigation items easily: ```elixir # Simple navigation item NavBuddy.nav_item("home", "Home", "/") # => %{id: "home", label: "Home", navigate: "/"} # With additional options NavBuddy.nav_item("admin", "Admin", "/admin", permissions: "admin", icon: "admin-icon") # => %{id: "admin", label: "Admin", navigate: "/admin", permissions: "admin", icon: "admin-icon"} # Dropdown item dropdown_items = [ NavBuddy.nav_item("web", "Web Apps", "/products/web"), NavBuddy.nav_item("mobile", "Mobile Apps", "/products/mobile") ] NavBuddy.dropdown_item("products", "Products", dropdown_items) # Mega menu item categories = %{ "Frontend" => [NavBuddy.nav_item("react", "React", "/tools/react")], "Backend" => [NavBuddy.nav_item("elixir", "Elixir", "/tools/elixir")] } NavBuddy.mega_menu_item("tools", "Tools", categories) ``` ### Validation and Error Handling NavBuddy provides comprehensive validation with helpful error messages: ```elixir # Invalid item - missing required fields NavBuddy.validate_nav_item(%{label: "Home"}) # => {:error, "Navigation item missing required field 'id'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"} # Invalid field type NavBuddy.validate_nav_item(%{id: "home", label: "Home", active: "yes"}) # => {:error, "Field 'active' must be a boolean, got: \"yes\""} # Conflicting navigation types NavBuddy.validate_nav_item(%{id: "item", label: "Item", dropdown: [], mega_menu: %{}}) # => {:error, "Navigation item cannot have both 'dropdown' and 'mega_menu'. Choose one or the other."} ``` ### Simple Links ```elixir %{id: "home", label: "Home", navigate: "/", icon: "home"} ``` ### Single-Layer Dropdowns ```elixir %{ id: "products", label: "Products", navigate: "/products", icon: "package", dropdown: [ %{id: "web", label: "Web Development", navigate: "/products/web"}, %{id: "mobile", label: "Mobile Apps", navigate: "/products/mobile"} ] } ``` ### Multi-Layer Dropdowns ```elixir %{ id: "services", label: "Services", dropdown: [ %{ id: "development", label: "Development", dropdown: [ %{id: "frontend", label: "Frontend", navigate: "/services/frontend"}, %{id: "backend", label: "Backend", navigate: "/services/backend"} ] } ] } ``` ### Mega Menus ```elixir %{ id: "solutions", label: "Solutions", mega_menu: %{ title: "Our Solutions", description: "Comprehensive business solutions", sections: [ %{ title: "For Small Business", description: "Perfect for startups", items: [ %{id: "starter", label: "Starter Package", navigate: "/solutions/starter"}, %{id: "growth", label: "Growth Package", navigate: "/solutions/growth"} ] }, %{ title: "For Enterprise", description: "Scalable solutions", items: [ %{id: "enterprise", label: "Enterprise Suite", navigate: "/solutions/enterprise"}, %{id: "custom", label: "Custom Solutions", navigate: "/solutions/custom"} ] } ] } } ``` ## Permissions-Based Navigation NavBuddy supports permissions-based filtering to control which navigation items are visible to users. Add a `permissions` key to any navigation item to require specific permissions. ### Basic Permissions ```elixir navigation_items = [ %{id: "home", label: "Home", navigate: "/"}, %{id: "dashboard", label: "Dashboard", navigate: "/dashboard", permissions: "user"}, %{id: "admin", label: "Admin Panel", navigate: "/admin", permissions: "admin"} ] ``` ### Multiple Required Permissions Use a list to require multiple permissions (user must have ALL permissions): ```elixir navigation_items = [ %{ id: "sensitive", label: "Sensitive Data", navigate: "/sensitive", permissions: ["admin", "data_access"] # Requires BOTH permissions } ] ``` ### Nested Structure Permissions Permissions work with dropdowns and mega-menus: ```elixir navigation_items = [ %{ id: "reports", label: "Reports", dropdown: [ %{id: "basic_reports", label: "Basic Reports", navigate: "/reports/basic"}, %{id: "admin_reports", label: "Admin Reports", navigate: "/reports/admin", permissions: "admin"}, %{id: "analytics", label: "Analytics", navigate: "/reports/analytics", permissions: ["analyst", "premium"]} ] } ] ``` ### Using in LiveView Pass user permissions to the component: ```elixir # In your LiveView mount/3 or assign def mount(_params, session, socket) do current_user = get_current_user(session) user_permissions = get_user_permissions(current_user) socket = assign(socket, user_permissions: user_permissions) {:ok, socket} end # In your template ``` ### Permission Examples ```elixir # String permission - user needs "admin" permission %{id: "admin", label: "Admin", navigate: "/admin", permissions: "admin"} # List of permissions - user needs ALL listed permissions %{id: "sensitive", label: "Sensitive", navigate: "/sensitive", permissions: ["admin", "security"]} # No permissions - visible to everyone %{id: "home", label: "Home", navigate: "/"} # nil permissions - same as no permissions key %{id: "public", label: "Public", navigate: "/public", permissions: nil} ``` The filtering automatically: - Shows items with no `permissions` key to everyone - Shows items with `permissions: nil` to everyone - Requires exact string match for string permissions - Requires ALL permissions in list for list permissions - Recursively filters dropdown and mega-menu items - Removes parent items if all children are filtered out ## Configuration Create a configuration struct: ```elixir config = %NavBuddy.Config{ orientation: :horizontal, # :horizontal | :vertical theme: :auto, # :light | :dark | :auto dropdown_trigger: :hover, # :hover | :click mobile_behavior: :drawer, # :drawer | :collapse | :overlay animations: true, # boolean accessibility: true, # boolean mobile_breakpoint: 768, # pixels max_dropdown_depth: 5, # levels auto_close_delay: 300, # milliseconds touch_gestures: true, # boolean analytics: false # boolean } ``` Use with your navigation: ```elixir <.nav_menu items={@navigation_items} config={config} current_path={@current_path} /> ``` ## LiveView Integration NavBuddy includes comprehensive LiveView helpers: ```elixir defmodule MyAppWeb.PageLive do use MyAppWeb, :live_view use NavBuddy.LiveHelpers # Adds event handlers def mount(_params, _session, socket) do socket = socket |> assign_navbuddy_state() |> assign(:navigation_items, navigation_items()) {:ok, socket} end # Event handlers are automatically available: # handle_event("navbuddy:item_click", ...) # handle_event("navbuddy:dropdown_toggle", ...) # handle_event("navbuddy:mobile_toggle", ...) end ``` ## Mobile Optimization ### Drawer Mode (Default) Slide-out navigation drawer on mobile: ```elixir %NavBuddy.Config{mobile_behavior: :drawer} ``` ### Collapse Mode Collapsible menu sections: ```elixir %NavBuddy.Config{mobile_behavior: :collapse} ``` ### Overlay Mode Full-screen navigation overlay: ```elixir %NavBuddy.Config{mobile_behavior: :overlay} ``` ## Theming ### Automatic Theme Detection ```elixir %NavBuddy.Config{theme: :auto} # Follows system preference ``` ### Manual Theme Selection ```elixir %NavBuddy.Config{theme: :light} # or :dark ``` ### Custom Themes Override CSS custom properties: ```css :root { --navbuddy-bg-primary: #your-color; --navbuddy-text-primary: #your-color; --navbuddy-accent-primary: #your-color; } ``` ## Accessibility Features NavBuddy is built with accessibility in mind: - **ARIA Support**: Proper ARIA labels, roles, and states - **Keyboard Navigation**: Full keyboard support with arrow keys - **Screen Readers**: Compatible with screen reader software - **Focus Management**: Intelligent focus handling - **High Contrast**: Support for high contrast themes - **Reduced Motion**: Respects `prefers-reduced-motion` ### Keyboard Navigation - **Arrow Keys**: Navigate between menu items - **Enter/Space**: Activate menu items - **Escape**: Close menus and return focus - **Home/End**: Jump to first/last items - **Tab**: Standard tab navigation ## Breadcrumbs Automatic breadcrumb generation: ```elixir <.nav_breadcrumbs items={@navigation_items} current_path={@current_path} /> ``` ## Advanced Features ### Smart Positioning Automatically adjusts dropdown and mega menu positions to stay within viewport: ```elixir %NavBuddy.Config{smart_positioning: true} ``` ### Touch Gestures Mobile-optimized touch interactions: ```elixir %NavBuddy.Config{touch_gestures: true} ``` ### Analytics Integration Built-in event tracking: ```elixir %NavBuddy.Config{analytics: true} # Events emitted: # navbuddy:item_click # navbuddy:dropdown_open # navbuddy:dropdown_close # navbuddy:mobile_toggle ``` ### Animation Control Control animations: ```elixir %NavBuddy.Config{animations: true, auto_close_delay: 300} ``` ## CSS Classes NavBuddy provides semantic CSS classes for customization: - `.navbuddy-nav` - Main navigation container - `.navbuddy-nav-item` - Navigation item wrapper - `.navbuddy-nav-link` - Navigation links - `.navbuddy-dropdown` - Dropdown menus - `.navbuddy-mega-menu` - Mega menus - `.navbuddy-mobile-toggle` - Mobile menu toggle - `.navbuddy-breadcrumbs` - Breadcrumb navigation ## JavaScript API Access the JavaScript API directly: ```javascript // Initialize manually const nav = new NavBuddy(document.querySelector(".navbuddy-nav"), { smartPositioning: true, keyboardNavigation: true, analytics: true, }); // Methods nav.openDropdown("products"); nav.closeMegaMenu("solutions"); nav.toggleMobileNav(); nav.closeAllMenus(); // Events nav.trackEvent("custom_event", { custom: "data" }); // State const state = nav.getState(); ``` ## Browser Support - Chrome 90+ - Firefox 88+ - Safari 14+ - Edge 90+ ## Examples Check out the included example navigation structure: ```elixir NavBuddy.example_navigation() ``` This returns a comprehensive navigation structure showcasing all features: - Simple links with icons - Single and multi-level dropdowns - Mega menus with grid layouts - Mixed navigation types ## Contributing 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ## Changelog ### v0.1.0 (2025-01-25) - Initial release - Advanced dropdown and mega menu support - Mobile-responsive design - Full accessibility implementation - Comprehensive theming system - LiveView integration - Touch gesture support - Analytics integration ## License MIT License - see [LICENSE](LICENSE) for details. ## Acknowledgments - Phoenix LiveView team for the excellent framework - Tailwind CSS for design inspiration - Accessibility guidelines from WCAG 2.1 --- **Made with ❤️ for the Phoenix community**