Enable universal navigation

This commit is contained in:
Claudio Ortolina
2026-02-26 15:10:40 +00:00
parent be3a3bbb1c
commit a76ae7169f
4 changed files with 166 additions and 1 deletions
@@ -159,6 +159,45 @@ defmodule MusicLibraryWeb.SearchComponents do
"""
end
@doc """
Renders a search result item for navigation links.
## Examples
<.search_result_navigation label="Collection" icon="hero-circle-stack" />
"""
attr :label, :string, required: true
attr :icon, :string, required: true
attr :rest, :global, include: ~w(phx-click phx-value-path)
def search_result_navigation(assigns) do
~H"""
<div
class={[
"p-3 rounded-lg cursor-pointer transition-colors",
"hover:bg-zinc-50 dark:hover:bg-zinc-700",
"aria-selected:bg-zinc-200 dark:aria-selected:bg-zinc-700"
]}
role="option"
{@rest}
>
<div class="flex items-center space-x-3">
<div class="shrink-0 w-8 h-8 rounded-md bg-zinc-100 dark:bg-zinc-700 flex items-center justify-center">
<.icon name={@icon} class="h-4 w-4 text-zinc-500 dark:text-zinc-400" />
</div>
<div class="min-w-0 flex-1">
<p class="text-sm font-medium text-zinc-900 dark:text-zinc-100">
{@label}
</p>
</div>
<span class="text-xs text-zinc-400 dark:text-zinc-500">
{gettext("Go to →")}
</span>
</div>
</div>
"""
end
@doc """
Renders a search result group with a title and items.
@@ -39,6 +39,20 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
<.no_results :if={@search_query != "" and @total_results == 0} query={@search_query} />
<div :if={@total_results > 0} class="max-h-148 md:max-h-164 overflow-y-auto mt-4">
<.search_result_group
:if={length(@navigation_links_results) > 0}
title={gettext("Go to")}
count={length(@navigation_links_results)}
>
<.search_result_navigation
:for={link <- @navigation_links_results}
label={link.label}
icon={link.icon}
phx-click="navigate_to_link"
phx-value-path={link.path}
phx-target={@myself}
/>
</.search_result_group>
<.search_result_group
:if={length(@search_results.artists) > 0}
title="Artists"
@@ -206,9 +220,11 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
query ->
search_results = Search.universal_search(query, limit: 5)
search_counts = Search.search_counts(query)
navigation_links_results = filter_navigation_links(query)
total_results =
length(search_results.collection) +
length(navigation_links_results) +
length(search_results.collection) +
length(search_results.wishlist) +
length(search_results.artists) +
length(search_results.record_sets)
@@ -218,6 +234,7 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
|> assign(:search_query, query)
|> assign(:search_results, search_results)
|> assign(:search_counts, search_counts)
|> assign(:navigation_links_results, navigation_links_results)
|> assign(:total_results, total_results)}
end
end
@@ -293,6 +310,14 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
|> push_navigate(to: ~p"/record-sets?query=#{query}")}
end
@impl true
def handle_event("navigate_to_link", %{"path" => path}, socket) do
{:noreply,
socket
|> assign(:show_modal, false)
|> push_navigate(to: path)}
end
defp reset(socket) do
socket
|> assign(:search_query, "")
@@ -303,6 +328,87 @@ defmodule MusicLibraryWeb.UniversalSearchLive.Index do
artists_count: 0,
record_sets_count: 0
})
|> assign(:navigation_links_results, [])
|> assign(:total_results, 0)
end
defp filter_navigation_links(query) do
downcased = String.downcase(query)
Enum.filter(navigation_links(), fn link ->
String.contains?(String.downcase(link.label), downcased) or
Enum.any?(link.keywords, &String.contains?(&1, downcased))
end)
end
defp navigation_links do
[
%{
label: "Stats",
path: ~p"/",
icon: "hero-chart-pie",
keywords: ["stats", "dashboard", "home"]
},
%{
label: "Collection",
path: ~p"/collection",
icon: "hero-circle-stack",
keywords: ["collection", "collected", "records"]
},
%{
label: "Wishlist",
path: ~p"/wishlist",
icon: "hero-star",
keywords: ["wishlist", "wish", "want"]
},
%{
label: "Sets",
path: ~p"/record-sets",
icon: "hero-rectangle-stack",
keywords: ["sets", "record sets", "groups"]
},
%{
label: "Scrobble Anything",
path: ~p"/scrobble",
icon: "hero-play",
keywords: ["scrobble", "play"]
},
%{
label: "Scrobbled Tracks",
path: ~p"/scrobbled-tracks",
icon: "hero-musical-note",
keywords: ["scrobbled", "tracks", "history", "listening"]
},
%{
label: "Scrobble Rules",
path: ~p"/scrobble-rules",
icon: "hero-adjustments-horizontal",
keywords: ["scrobble rules", "rules", "remap"]
},
%{
label: "Online Store Templates",
path: ~p"/online-store-templates",
icon: "hero-building-storefront",
keywords: ["store", "templates", "online", "buy"]
},
%{
label: "Live Dashboard",
path: ~p"/dev/dashboard",
icon: "hero-chart-bar",
keywords: ["live dashboard", "telemetry", "metrics"]
},
%{
label: "Oban",
path: ~p"/dev/oban",
icon: "hero-cog",
keywords: ["oban", "jobs", "workers", "queue"]
},
%{
label: "Maintenance",
path: ~p"/dev/maintenance",
icon: "hero-wrench-screwdriver",
keywords: ["maintenance", "admin", "vacuum"]
}
]
end
end
+10
View File
@@ -2021,3 +2021,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Wikipedia"
msgstr ""
#: lib/music_library_web/live/universal_search_live/index.ex
#, elixir-autogen, elixir-format
msgid "Go to"
msgstr ""
#: lib/music_library_web/components/search_components.ex
#, elixir-autogen, elixir-format
msgid "Go to →"
msgstr ""
+10
View File
@@ -2021,3 +2021,13 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy
msgid "Wikipedia"
msgstr ""
#: lib/music_library_web/live/universal_search_live/index.ex
#, elixir-autogen, elixir-format
msgid "Go to"
msgstr ""
#: lib/music_library_web/components/search_components.ex
#, elixir-autogen, elixir-format
msgid "Go to →"
msgstr ""