Packages

Advanced configurable navigation component for Phoenix LiveView with native DaisyUI integration, Tailwind CSS support, permissions filtering, dropdowns, mega menus, and modern responsive design. Optimized for Phoenix 1.8+ and LiveView 1.1+.

Current section

8 Versions

Jump to

Compare versions

5 files changed
+178 additions
-83 deletions
  @@ -81,10 +81,12 @@ NavBuddy supports various navigation structures with comprehensive validation an
81 81 ### Required vs Optional Fields
82 82
83 83 **Required fields:**
84 +
84 85 - `id` (string) - Unique identifier for the navigation item
85 86 - `label` (string) - Display text for the navigation item
86 87
87 88 **Optional fields:**
89 +
88 90 - `navigate` (string) - Path to navigate to (required for clickable items)
89 91 - `icon` (string) - Icon class or identifier
90 92 - `dropdown` (list) - List of child navigation items
  @@ -113,7 +115,7 @@ dropdown_items = [
113 115 ]
114 116 NavBuddy.dropdown_item("products", "Products", dropdown_items)
115 117
116 - # Mega menu item
118 + # Mega menu item
117 119 categories = %{
118 120 "Frontend" => [NavBuddy.nav_item("react", "React", "/tools/react")],
119 121 "Backend" => [NavBuddy.nav_item("elixir", "Elixir", "/tools/elixir")]
  @@ -2,7 +2,7 @@
2 2 [{<<"Docs">>,<<"https://hexdocs.pm/navbuddy">>},
3 3 {<<"GitHub">>,<<"https://github.com/SangRJ/navbuddy">>}]}.
4 4 {<<"name">>,<<"navbuddy">>}.
5 - {<<"version">>,<<"0.2.0">>}.
5 + {<<"version">>,<<"0.2.3">>}.
6 6 {<<"description">>,
7 7 <<"Advanced configurable navigation component for Phoenix LiveView applications with permissions-based filtering, comprehensive validation, convenient helper functions, and support for dropdowns, mega menus, and mobile-responsive design.">>}.
8 8 {<<"elixir">>,<<"~> 1.14">>}.
  @@ -266,16 +266,19 @@ defmodule NavBuddy do
266 266 {:error, "Navigation items must be a list, got: #{inspect(items)}"}
267 267 end
268 268
269 - defp validate_required_fields(%{id: id, label: label}) when is_binary(id) and is_binary(label) do
269 + defp validate_required_fields(%{id: id, label: label})
270 + when is_binary(id) and is_binary(label) do
270 271 :ok
271 272 end
272 273
273 274 defp validate_required_fields(%{id: id}) when is_binary(id) do
274 - {:error, "Navigation item missing required field 'label'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
275 + {:error,
276 + "Navigation item missing required field 'label'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
275 277 end
276 278
277 279 defp validate_required_fields(%{label: label}) when is_binary(label) do
278 - {:error, "Navigation item missing required field 'id'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
280 + {:error,
281 + "Navigation item missing required field 'id'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
279 282 end
280 283
281 284 defp validate_required_fields(%{id: id}) do
  @@ -287,7 +290,8 @@ defmodule NavBuddy do
287 290 end
288 291
289 292 defp validate_required_fields(_) do
290 - {:error, "Navigation item missing required fields 'id' and 'label'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
293 + {:error,
294 + "Navigation item missing required fields 'id' and 'label'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
291 295 end
292 296
293 297 defp validate_field_types(item) do
  @@ -301,55 +305,82 @@ defmodule NavBuddy do
301 305
302 306 defp validate_field_type(:navigate, value) when is_binary(value), do: :ok
303 307 defp validate_field_type(:navigate, nil), do: :ok
304 - defp validate_field_type(:navigate, value), do: {:error, "Field 'navigate' must be a string, got: #{inspect(value)}"}
308 +
309 + defp validate_field_type(:navigate, value),
310 + do: {:error, "Field 'navigate' must be a string, got: #{inspect(value)}"}
305 311
306 312 defp validate_field_type(:icon, value) when is_binary(value), do: :ok
307 313 defp validate_field_type(:icon, nil), do: :ok
308 - defp validate_field_type(:icon, value), do: {:error, "Field 'icon' must be a string, got: #{inspect(value)}"}
314 +
315 + defp validate_field_type(:icon, value),
316 + do: {:error, "Field 'icon' must be a string, got: #{inspect(value)}"}
309 317
310 318 defp validate_field_type(:active, value) when is_boolean(value), do: :ok
311 319 defp validate_field_type(:active, nil), do: :ok
312 - defp validate_field_type(:active, value), do: {:error, "Field 'active' must be a boolean, got: #{inspect(value)}"}
320 +
321 + defp validate_field_type(:active, value),
322 + do: {:error, "Field 'active' must be a boolean, got: #{inspect(value)}"}
313 323
314 324 defp validate_field_type(:disabled, value) when is_boolean(value), do: :ok
315 325 defp validate_field_type(:disabled, nil), do: :ok
316 - defp validate_field_type(:disabled, value), do: {:error, "Field 'disabled' must be a boolean, got: #{inspect(value)}"}
326 +
327 + defp validate_field_type(:disabled, value),
328 + do: {:error, "Field 'disabled' must be a boolean, got: #{inspect(value)}"}
317 329
318 330 defp validate_field_type(:permissions, value) when is_binary(value), do: :ok
331 +
319 332 defp validate_field_type(:permissions, value) when is_list(value) do
320 333 if Enum.all?(value, &is_binary/1) do
321 334 :ok
322 335 else
323 - {:error, "Field 'permissions' when a list must contain only strings, got: #{inspect(value)}"}
336 + {:error,
337 + "Field 'permissions' when a list must contain only strings, got: #{inspect(value)}"}
324 338 end
325 339 end
340 +
326 341 defp validate_field_type(:permissions, nil), do: :ok
327 - defp validate_field_type(:permissions, value), do: {:error, "Field 'permissions' must be a string or list of strings, got: #{inspect(value)}"}
342 +
343 + defp validate_field_type(:permissions, value),
344 + do:
345 + {:error, "Field 'permissions' must be a string or list of strings, got: #{inspect(value)}"}
328 346
329 347 defp validate_field_type(:dropdown, value) when is_list(value), do: :ok
330 348 defp validate_field_type(:dropdown, nil), do: :ok
331 - defp validate_field_type(:dropdown, value), do: {:error, "Field 'dropdown' must be a list of navigation items, got: #{inspect(value)}"}
349 +
350 + defp validate_field_type(:dropdown, value),
351 + do: {:error, "Field 'dropdown' must be a list of navigation items, got: #{inspect(value)}"}
332 352
333 353 defp validate_field_type(:mega_menu, value) when is_map(value), do: :ok
334 354 defp validate_field_type(:mega_menu, nil), do: :ok
335 - defp validate_field_type(:mega_menu, value), do: {:error, "Field 'mega_menu' must be a map, got: #{inspect(value)}"}
355 +
356 + defp validate_field_type(:mega_menu, value),
357 + do: {:error, "Field 'mega_menu' must be a map, got: #{inspect(value)}"}
336 358
337 359 # Allow id and label since they're already validated above
338 360 defp validate_field_type(:id, _), do: :ok
339 361 defp validate_field_type(:label, _), do: :ok
340 362
341 - defp validate_field_type(key, value), do: {:error, "Unknown field '#{key}' with value #{inspect(value)}. Allowed fields: id, label, navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
363 + defp validate_field_type(key, value),
364 + do:
365 + {:error,
366 + "Unknown field '#{key}' with value #{inspect(value)}. Allowed fields: id, label, navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
342 367
343 - defp validate_navigation_logic(%{dropdown: dropdown, mega_menu: mega_menu}) when not is_nil(dropdown) and not is_nil(mega_menu) do
344 - {:error, "Navigation item cannot have both 'dropdown' and 'mega_menu'. Choose one or the other."}
368 + defp validate_navigation_logic(%{dropdown: dropdown, mega_menu: mega_menu})
369 + when not is_nil(dropdown) and not is_nil(mega_menu) do
370 + {:error,
371 + "Navigation item cannot have both 'dropdown' and 'mega_menu'. Choose one or the other."}
345 372 end
346 373
347 - defp validate_navigation_logic(%{dropdown: dropdown, navigate: navigate}) when not is_nil(dropdown) and not is_nil(navigate) do
348 - {:error, "Navigation item with 'dropdown' should not have 'navigate' field. Dropdown items should navigate through their child items."}
374 + defp validate_navigation_logic(%{dropdown: dropdown, navigate: navigate})
375 + when not is_nil(dropdown) and not is_nil(navigate) do
376 + {:error,
377 + "Navigation item with 'dropdown' should not have 'navigate' field. Dropdown items should navigate through their child items."}
349 378 end
350 379
351 - defp validate_navigation_logic(%{mega_menu: mega_menu, navigate: navigate}) when not is_nil(mega_menu) and not is_nil(navigate) do
352 - {:error, "Navigation item with 'mega_menu' should not have 'navigate' field. Mega menu items should navigate through their child items."}
380 + defp validate_navigation_logic(%{mega_menu: mega_menu, navigate: navigate})
381 + when not is_nil(mega_menu) and not is_nil(navigate) do
382 + {:error,
383 + "Navigation item with 'mega_menu' should not have 'navigate' field. Mega menu items should navigate through their child items."}
353 384 end
354 385
355 386 defp validate_navigation_logic(_), do: :ok
  @@ -363,7 +394,8 @@ defmodule NavBuddy do
363 394 end
364 395 end
365 396
366 - defp validate_dropdown(dropdown), do: {:error, "Dropdown must be a list of navigation items, got: #{inspect(dropdown)}"}
397 + defp validate_dropdown(dropdown),
398 + do: {:error, "Dropdown must be a list of navigation items, got: #{inspect(dropdown)}"}
367 399
368 400 defp validate_mega_menu(nil), do: {:ok, nil}
369 401
  @@ -377,18 +409,23 @@ defmodule NavBuddy do
377 409 end
378 410 end
379 411
380 - defp validate_mega_menu(mega_menu), do: {:error, "Mega menu must be a map, got: #{inspect(mega_menu)}"}
412 + defp validate_mega_menu(mega_menu),
413 + do: {:error, "Mega menu must be a map, got: #{inspect(mega_menu)}"}
381 414
382 415 defp validate_simple_mega_menu(mega_menu) do
383 416 Enum.reduce_while(mega_menu, {:ok, mega_menu}, fn {category, items}, {:ok, acc} ->
384 417 case validate_nav_items(items) do
385 - {:ok, _} -> {:cont, {:ok, acc}}
386 - {:error, reason} -> {:halt, {:error, "Invalid items in mega menu category '#{category}': #{reason}"}}
418 + {:ok, _} ->
419 + {:cont, {:ok, acc}}
420 +
421 + {:error, reason} ->
422 + {:halt, {:error, "Invalid items in mega menu category '#{category}': #{reason}"}}
387 423 end
388 424 end)
389 425 end
390 426
391 - defp validate_structured_mega_menu(%{title: title, sections: sections}) when is_binary(title) and is_list(sections) do
427 + defp validate_structured_mega_menu(%{title: title, sections: sections})
428 + when is_binary(title) and is_list(sections) do
392 429 case validate_mega_menu_sections(sections) do
393 430 {:ok, _} -> {:ok, %{title: title, sections: sections}}
394 431 {:error, reason} -> {:error, "Invalid mega menu sections: #{reason}"}
  @@ -396,11 +433,13 @@ defmodule NavBuddy do
396 433 end
397 434
398 435 defp validate_structured_mega_menu(%{sections: sections}) when is_list(sections) do
399 - {:error, "Structured mega menu missing required field 'title'. Required: title (string), sections (list). Optional: description (string)"}
436 + {:error,
437 + "Structured mega menu missing required field 'title'. Required: title (string), sections (list). Optional: description (string)"}
400 438 end
401 439
402 440 defp validate_structured_mega_menu(%{title: title}) when is_binary(title) do
403 - {:error, "Structured mega menu missing required field 'sections'. Required: title (string), sections (list). Optional: description (string)"}
441 + {:error,
442 + "Structured mega menu missing required field 'sections'. Required: title (string), sections (list). Optional: description (string)"}
404 443 end
405 444
406 445 defp validate_structured_mega_menu(%{title: title}) do
  @@ -412,7 +451,8 @@ defmodule NavBuddy do
412 451 end
413 452
414 453 defp validate_structured_mega_menu(mega_menu) do
415 - {:error, "Structured mega menu missing required fields. Required: title (string), sections (list). Optional: description (string). Got: #{inspect(mega_menu)}"}
454 + {:error,
455 + "Structured mega menu missing required fields. Required: title (string), sections (list). Optional: description (string). Got: #{inspect(mega_menu)}"}
416 456 end
417 457
418 458 defp validate_mega_menu_sections(sections) do
  @@ -428,7 +468,8 @@ defmodule NavBuddy do
428 468 end
429 469 end
430 470
431 - defp validate_mega_menu_section(%{title: title, items: items}) when is_binary(title) and is_list(items) do
471 + defp validate_mega_menu_section(%{title: title, items: items})
472 + when is_binary(title) and is_list(items) do
432 473 case validate_nav_items(items) do
433 474 {:ok, _} -> {:ok, %{title: title, items: items}}
434 475 {:error, reason} -> {:error, "Invalid items in mega menu section '#{title}': #{reason}"}
  @@ -436,11 +477,13 @@ defmodule NavBuddy do
436 477 end
437 478
438 479 defp validate_mega_menu_section(%{items: items}) when is_list(items) do
439 - {:error, "Mega menu section missing required field 'title'. Required: title (string), items (list). Optional: description (string)"}
480 + {:error,
481 + "Mega menu section missing required field 'title'. Required: title (string), items (list). Optional: description (string)"}
440 482 end
441 483
442 484 defp validate_mega_menu_section(%{title: title}) when is_binary(title) do
443 - {:error, "Mega menu section missing required field 'items'. Required: title (string), items (list). Optional: description (string)"}
485 + {:error,
486 + "Mega menu section missing required field 'items'. Required: title (string), items (list). Optional: description (string)"}
444 487 end
445 488
446 489 defp validate_mega_menu_section(%{title: title}) do
  @@ -452,7 +495,8 @@ defmodule NavBuddy do
452 495 end
453 496
454 497 defp validate_mega_menu_section(section) do
455 - {:error, "Mega menu section must have 'title' (string) and 'items' (list). Got: #{inspect(section)}"}
498 + {:error,
499 + "Mega menu section must have 'title' (string) and 'items' (list). Got: #{inspect(section)}"}
456 500 end
457 501
458 502 @doc """
  @@ -468,9 +512,9 @@ defmodule NavBuddy do
468 512 """
469 513 def nav_item(id, label, navigate \\ nil, opts \\ []) do
470 514 base = %{id: id, label: label}
471 -
515 +
472 516 base = if navigate, do: Map.put(base, :navigate, navigate), else: base
473 -
517 +
474 518 Enum.reduce(opts, base, fn {key, value}, acc ->
475 519 Map.put(acc, key, value)
476 520 end)
  @@ -490,7 +534,7 @@ defmodule NavBuddy do
490 534 """
491 535 def dropdown_item(id, label, dropdown_items, opts \\ []) do
492 536 base = %{id: id, label: label, dropdown: dropdown_items}
493 -
537 +
494 538 Enum.reduce(opts, base, fn {key, value}, acc ->
495 539 Map.put(acc, key, value)
496 540 end)
  @@ -510,7 +554,7 @@ defmodule NavBuddy do
510 554 """
511 555 def mega_menu_item(id, label, categories, opts \\ []) do
512 556 base = %{id: id, label: label, mega_menu: categories}
513 -
557 +
514 558 Enum.reduce(opts, base, fn {key, value}, acc ->
515 559 Map.put(acc, key, value)
516 560 end)
  @@ -157,7 +157,7 @@ defmodule NavBuddy.Component do
157 157 ~H"""
158 158 <div class={@item_classes} {@item_attributes}>
159 159 <%= cond do %>
160 - <% @item.mega_menu -> %>
160 + <% Map.get(@item, :mega_menu) -> %>
161 161 <.mega_menu_trigger item={@item} config={@config} nav_id={@nav_id} />
162 162 <.mega_menu
163 163 menu={@item.mega_menu}
  @@ -166,7 +166,7 @@ defmodule NavBuddy.Component do
166 166 item_id={@item.id}
167 167 />
168 168
169 - <% @item.dropdown -> %>
169 + <% Map.get(@item, :dropdown) -> %>
170 170 <.dropdown_trigger item={@item} config={@config} nav_id={@nav_id} />
171 171 <.dropdown_menu
172 172 items={@item.dropdown}
  @@ -192,26 +192,26 @@ defmodule NavBuddy.Component do
192 192
193 193 def nav_link(assigns) do
194 194 ~H"""
195 - <%= if @item.navigate do %>
195 + <%= if Map.get(@item, :navigate) do %>
196 196 <a
197 197 href="#"
198 - class={["navbuddy-nav-link", if(@item.active, do: "active"), if(@item.disabled, do: "disabled")]}
198 + class={["navbuddy-nav-link", if(Map.get(@item, :active), do: "active"), if(Map.get(@item, :disabled), do: "disabled")]}
199 199 role="menuitem"
200 - tabindex={if @item.disabled, do: "-1", else: "0"}
201 - aria-disabled={to_string(!!@item.disabled)}
202 - phx-click={JS.push("navbuddy:item_click", value: %{id: @item.id, navigate: @item.navigate})}
200 + tabindex={if Map.get(@item, :disabled), do: "-1", else: "0"}
201 + aria-disabled={to_string(!!Map.get(@item, :disabled))}
202 + phx-click={JS.push("navbuddy:item_click", value: %{id: @item.id, navigate: Map.get(@item, :navigate)})}
203 203 >
204 - <.nav_icon :if={@item.icon} icon={@item.icon} />
204 + <.nav_icon :if={Map.get(@item, :icon)} icon={Map.get(@item, :icon)} />
205 205 <span class="navbuddy-nav-text">{@item.label}</span>
206 206 </a>
207 207 <% else %>
208 208 <span
209 - class={["navbuddy-nav-link", "no-navigate", if(@item.disabled, do: "disabled")]}
209 + class={["navbuddy-nav-link", "no-navigate", if(Map.get(@item, :disabled), do: "disabled")]}
210 210 role="menuitem"
211 - tabindex={if @item.disabled, do: "-1", else: "0"}
212 - aria-disabled={to_string(!!@item.disabled)}
211 + tabindex={if Map.get(@item, :disabled), do: "-1", else: "0"}
212 + aria-disabled={to_string(!!Map.get(@item, :disabled))}
213 213 >
214 - <.nav_icon :if={@item.icon} icon={@item.icon} />
214 + <.nav_icon :if={Map.get(@item, :icon)} icon={Map.get(@item, :icon)} />
215 215 <span class="navbuddy-nav-text">{@item.label}</span>
216 216 </span>
217 217 <% end %>
  @@ -328,6 +328,27 @@ defmodule NavBuddy.Component do
328 328
329 329 @doc """
330 330 Renders a mega menu.
331 +
332 + Supports two formats:
333 +
334 + 1. Simple format: %{"Category Name" => [nav_items]}
335 + 2. Structured format: %{title: "Title", description: "Description", sections: [%{title: "Section", items: [nav_items]}]}
336 +
337 + ## Examples
338 +
339 + # Simple mega menu
340 + <.mega_menu
341 + menu={%{"Frontend" => [nav_items], "Backend" => [nav_items]}}
342 + config={@config}
343 + nav_id="main-nav"
344 + item_id="tools" />
345 +
346 + # Structured mega menu
347 + <.mega_menu
348 + menu={%{title: "Our Tools", sections: [%{title: "Frontend", items: [nav_items]}]}}
349 + config={@config}
350 + nav_id="main-nav"
351 + item_id="tools" />
331 352 """
332 353 attr(:menu, :map, required: true)
333 354 attr(:config, NavBuddy.Config, required: true)
  @@ -346,42 +367,70 @@ defmodule NavBuddy.Component do
346 367 aria-labelledby={"#{@nav_id}-#{@item_id}-trigger"}
347 368 >
348 369 <div class="navbuddy-mega-menu-container">
349 - <div class="navbuddy-mega-menu-header">
350 - <h3 class="navbuddy-mega-menu-title">{@menu.title}</h3>
351 - <p :if={@menu.description} class="navbuddy-mega-menu-description">
352 - {@menu.description}
353 - </p>
354 - </div>
355 -
356 - <div class="navbuddy-mega-menu-grid">
357 - <div
358 - :for={section <- @menu.sections}
359 - class="navbuddy-mega-menu-section"
360 - role="group"
361 - aria-labelledby={"#{@menu_id}-section-#{section.title |> String.downcase() |> String.replace(~r/[^a-z0-9]/, "-")}"}
362 - >
363 - <h4
364 - id={"#{@menu_id}-section-#{section.title |> String.downcase() |> String.replace(~r/[^a-z0-9]/, "-")}"}
365 - class="navbuddy-mega-menu-section-title"
366 - >
367 - {section.title}
368 - </h4>
369 - <p :if={section.description} class="navbuddy-mega-menu-section-description">
370 - {section.description}
370 + <%= if is_structured_mega_menu?(@menu) do %>
371 + <div class="navbuddy-mega-menu-header">
372 + <h3 class="navbuddy-mega-menu-title">{@menu.title}</h3>
373 + <p :if={Map.get(@menu, :description)} class="navbuddy-mega-menu-description">
374 + {Map.get(@menu, :description)}
371 375 </p>
372 -
373 - <ul class="navbuddy-mega-menu-section-items">
374 - <li :for={item <- section.items} class="navbuddy-mega-menu-item" role="none">
375 - <.nav_link item={item} config={@config} />
376 - </li>
377 - </ul>
378 376 </div>
379 - </div>
377 +
378 + <div class="navbuddy-mega-menu-grid">
379 + <div
380 + :for={section <- @menu.sections}
381 + class="navbuddy-mega-menu-section"
382 + role="group"
383 + aria-labelledby={"#{@menu_id}-section-#{section.title |> String.downcase() |> String.replace(~r/[^a-z0-9]/, "-")}"}
384 + >
385 + <h4
386 + id={"#{@menu_id}-section-#{section.title |> String.downcase() |> String.replace(~r/[^a-z0-9]/, "-")}"}
387 + class="navbuddy-mega-menu-section-title"
388 + >
389 + {section.title}
390 + </h4>
391 + <p :if={Map.get(section, :description)} class="navbuddy-mega-menu-section-description">
392 + {Map.get(section, :description)}
393 + </p>
394 +
395 + <ul class="navbuddy-mega-menu-section-items">
396 + <li :for={item <- section.items} class="navbuddy-mega-menu-item" role="none">
397 + <.nav_link item={item} config={@config} />
398 + </li>
399 + </ul>
400 + </div>
401 + </div>
402 + <% else %>
403 + <div class="navbuddy-mega-menu-grid">
404 + <div
405 + :for={{category_name, items} <- @menu}
406 + class="navbuddy-mega-menu-section"
407 + role="group"
408 + aria-labelledby={"#{@menu_id}-category-#{category_name |> String.downcase() |> String.replace(~r/[^a-z0-9]/, "-")}"}
409 + >
410 + <h4
411 + id={"#{@menu_id}-category-#{category_name |> String.downcase() |> String.replace(~r/[^a-z0-9]/, "-")}"}
412 + class="navbuddy-mega-menu-section-title"
413 + >
414 + {category_name}
415 + </h4>
416 +
417 + <ul class="navbuddy-mega-menu-section-items">
418 + <li :for={item <- items} class="navbuddy-mega-menu-item" role="none">
419 + <.nav_link item={item} config={@config} />
420 + </li>
421 + </ul>
422 + </div>
423 + </div>
424 + <% end %>
380 425 </div>
381 426 </div>
382 427 """
383 428 end
384 429
430 + # Helper function to determine mega menu type
431 + defp is_structured_mega_menu?(%{title: _, sections: _}), do: true
432 + defp is_structured_mega_menu?(_), do: false
433 +
385 434 @doc """
386 435 Renders the mobile navigation toggle button.
387 436 """
  @@ -588,10 +637,10 @@ defmodule NavBuddy.Component do
588 637 defp assign_item_data(assigns) do
589 638 item_classes = [
590 639 "navbuddy-nav-item-wrapper",
591 - if(assigns.item.active, do: "active"),
592 - if(assigns.item.disabled, do: "disabled"),
593 - if(assigns.item.dropdown, do: "has-dropdown"),
594 - if(assigns.item.mega_menu, do: "has-mega-menu")
640 + if(Map.get(assigns.item, :active), do: "active"),
641 + if(Map.get(assigns.item, :disabled), do: "disabled"),
642 + if(Map.get(assigns.item, :dropdown), do: "has-dropdown"),
643 + if(Map.get(assigns.item, :mega_menu), do: "has-mega-menu")
595 644 ]
596 645
597 646 item_attributes = %{
  @@ -1,7 +1,7 @@
1 1 defmodule NavBuddy.MixProject do
2 2 use Mix.Project
3 3
4 - @version "0.2.0"
4 + @version "0.2.3"
5 5 @source_url "https://github.com/SangRJ/navbuddy"
6 6
7 7 def project do