Move record actions into menu

This commit is contained in:
Claudio Ortolina
2024-12-12 00:19:32 +03:00
parent 91b81de0f8
commit c3f973b1e1
3 changed files with 178 additions and 88 deletions
@@ -1,6 +1,8 @@
defmodule MusicLibraryWeb.CollectionLive.Show do
use MusicLibraryWeb, :live_view
alias Phoenix.LiveView.JS
alias MusicLibrary.Records
@impl true
@@ -148,4 +150,14 @@ defmodule MusicLibraryWeb.CollectionLive.Show do
defp human_datetime(dt) do
"#{dt.day}/#{dt.month}/#{dt.year}"
end
defp toggle_actions_menu(record_id) do
JS.toggle(to: "#actions-#{record_id}")
|> JS.toggle_class("pointer-events-none", to: "#records > li")
end
defp close_actions_menu(record_id) do
JS.hide(to: "#actions-#{record_id}")
|> JS.remove_class("pointer-events-none", to: "#records > li")
end
end
@@ -7,83 +7,150 @@
/>
</div>
<div class="mt-4 md:mt-0">
<h1 class="text-base font-medium leading-6 text-zinc-700">
<.link
:for={artist <- @record.artists}
class="text-zinc-700 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
navigate={~p"/artists/#{artist.musicbrainz_id}"}
<div class="mt-4 md:mt-0 flex justify-between">
<div>
<h1 class="text-base font-medium leading-6 text-zinc-700">
<.link
:for={artist <- @record.artists}
class="text-zinc-700 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
navigate={~p"/artists/#{artist.musicbrainz_id}"}
>
{artist.name}
</.link>
</h1>
<h2 class="mt-1 flex font-semibold text-base sm:text-lg leading-5 text-zinc-700 dark:text-zinc-300 text-wrap">
{@record.title}
</h2>
<p class="mt-2 text-sm leading-5 text-zinc-500 dark:text-zinc-400">
{Records.Record.format_release(@record.release)} · {Records.Record.format_long_label(
@record.format
)} · {Records.Record.type_long_label(@record.type)}
</p>
</div>
<div class="relative flex-none">
<button
type="button"
class="text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-300"
aria-expanded="false"
aria-haspopup="true"
phx-click={toggle_actions_menu(@record.id)}
>
{artist.name}
</.link>
</h1>
<h2 class="mt-1 flex font-semibold text-base sm:text-lg leading-5 text-zinc-700 dark:text-zinc-300 text-wrap">
{@record.title}
</h2>
<p class="mt-2 text-sm leading-5 text-zinc-500 dark:text-zinc-400">
{Records.Record.format_release(@record.release)} · {Records.Record.format_long_label(
@record.format
)} · {Records.Record.type_long_label(@record.type)}
</p>
<!-- TODO: extract to a component -->
<nav class="mt-3 sm:mt-5 isolate inline-flex rounded-md shadow-sm">
<.link patch={~p"/collection/#{@record}/show/edit"} phx-click={JS.push_focus()}>
<.button type="button" class="relative inline-flex items-center rounded-r-none">
<span class="sr-only">{gettext("Open options")}</span>
<.icon
name="hero-ellipsis-vertical"
class="-mt-1 h-5 w-5"
aria-hidden="true"
data-slot="icon"
/>
</button>
<.focus_wrap
id={"actions-#{@record.id}"}
class={[
"hidden pointer-events-auto absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-zinc-800 py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
]}
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu-0-button"
tabindex="-1"
phx-click-away={close_actions_menu(@record.id)}
>
<.link
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
role="menuitem"
tabindex="-1"
id={"actions-#{@record.id}-edit"}
patch={~p"/collection/#{@record}/show/edit"}
>
<.icon
name="hero-pencil-square"
class="h-4 w-4 mr-1 phx-click-loading:animate-spin"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Edit")}
</.button>
</.link>
<.link phx-click={JS.push("refresh_cover", value: %{id: @record.id})}>
<.button type="button" class="relative -ml-px inline-flex items-center rounded-none">
<span class="sr-only">{gettext("Refresh")}</span>
</.link>
<.link
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
role="menuitem"
tabindex="-1"
id={"actions-#{@record.id}-refresh-cover"}
phx-click={JS.push("refresh_cover", value: %{id: @record.id})}
>
<.icon
name="hero-photo"
class="h-4 w-4 mr-1 phx-click-loading:animate-bounce"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Refresh cover")}
</.link>
<.link
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
role="menuitem"
tabindex="-1"
id={"actions-#{@record.id}-refresh-mb-data"}
phx-click={JS.push("refresh_musicbrainz_data", value: %{id: @record.id})}
>
<.icon
name="hero-arrow-path"
class="h-4 w-4 mr-1 phx-click-loading:animate-spin"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Cover")}
</.button>
</.link>
<.link phx-click={JS.push("refresh_musicbrainz_data", value: %{id: @record.id})}>
<.button type="button" class="relative -ml-px inline-flex items-center rounded-none">
<span class="sr-only">{gettext("Refresh")}</span>
<.icon
name="hero-arrow-path"
class="h-4 w-4 mr-1 phx-click-loading:animate-spin"
aria-hidden="true"
data-slot="icon"
/>
{gettext("MB Data")}
</.button>
</.link>
<.link phx-click={JS.push("populate_genres", value: %{id: @record.id})}>
<.button type="button" class="relative -ml-px inline-flex items-center rounded-none">
<span class="sr-only">{gettext("Populate")}</span>
{gettext("Refresh MB data")}
</.link>
<.link
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
role="menuitem"
tabindex="-1"
id={"actions-#{@record.id}-populate-genres"}
phx-click={JS.push("populate_genres", value: %{id: @record.id})}
>
<.icon
name="hero-sparkles"
class="h-4 w-4 mr-1 phx-click-loading:animate-shake"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Genres")}
</.button>
</.link>
<.link
phx-click={JS.push("delete", value: %{id: @record.id})}
data-confirm={gettext("Are you sure?")}
>
<.button
type="button"
class="relative -ml-px inline-flex items-center rounded-l-none !text-red-600 hover:!text-red-500 dark:!text-red-700 hover:dark:!text-red-500"
{gettext("Populate genres")}
</.link>
<.link
:if={!@record.purchased_at}
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
role="menuitem"
tabindex="-1"
id={"actions-#{@record.id}-purchase"}
phx-click={JS.push("purchase", value: %{id: @record.id})}
>
{gettext("Purchase")}
</.link>
<.link
class="block px-3 py-1 text-sm leading-6 text-red-900 hover:bg-red-50 dark:text-red-700 dark:hover:bg-red-900/30 dark:hover:text-red-600"
role="menuitem"
tabindex="-1"
id={"actions-#{@record.id}-delete"}
phx-click={JS.push("delete", value: %{id: @record.id})}
data-confirm={gettext("Are you sure?")}
>
<.icon
name="hero-trash"
class="h-4 w-4 mr-1 phx-click-loading:animate-spin"
aria-hidden="true"
data-slot="icon"
/>
{gettext("Delete")}
</.button>
</.link>
</nav>
</.link>
</.focus_wrap>
</div>
</div>
<div>
<%!-- TODO: replace with OSS version --%>
<dl class="mt-4 divide-y divide-zinc-100 dark:divide-slate-300/30">
<dl class="divide-y divide-zinc-100 dark:divide-slate-300/30">
<div class="py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm font-medium leading-6 text-zinc-900 dark:text-zinc-400">
{gettext("Genres")}
+41 -30
View File
@@ -17,7 +17,7 @@ msgid "Actions"
msgstr ""
#: lib/music_library_web/components/record_components.ex:179
#: lib/music_library_web/live/collection_live/show.html.heex:73
#: lib/music_library_web/live/collection_live/show.html.heex:138
#: lib/music_library_web/live/wishlist_live/show.html.heex:73
#, elixir-autogen, elixir-format
msgid "Are you sure?"
@@ -28,7 +28,7 @@ msgstr ""
msgid "Attempting to reconnect"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:173
#: lib/music_library_web/live/collection_live/show.html.heex:240
#, elixir-autogen, elixir-format
msgid "Back to records"
msgstr ""
@@ -52,8 +52,8 @@ msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex:14
#: lib/music_library_web/live/artist_live/show.html.heex:61
#: lib/music_library_web/live/collection_live/index.ex:78
#: lib/music_library_web/live/collection_live/show.ex:122
#: lib/music_library_web/live/collection_live/show.ex:139
#: lib/music_library_web/live/collection_live/show.ex:124
#: lib/music_library_web/live/collection_live/show.ex:141
#, elixir-autogen, elixir-format
msgid "Collection"
msgstr ""
@@ -64,15 +64,15 @@ msgid "Cover art"
msgstr ""
#: lib/music_library_web/components/record_components.ex:181
#: lib/music_library_web/live/collection_live/show.html.heex:79
#: lib/music_library_web/live/collection_live/show.html.heex:146
#: lib/music_library_web/live/wishlist_live/show.html.heex:79
#, elixir-autogen, elixir-format
msgid "Delete"
msgstr ""
#: lib/music_library_web/components/record_components.ex:159
#: lib/music_library_web/live/collection_live/show.ex:146
#: lib/music_library_web/live/collection_live/show.html.heex:32
#: lib/music_library_web/live/collection_live/show.ex:148
#: lib/music_library_web/live/collection_live/show.html.heex:70
#: lib/music_library_web/live/wishlist_live/show.ex:129
#: lib/music_library_web/live/wishlist_live/show.html.heex:32
#, elixir-autogen, elixir-format
@@ -102,8 +102,7 @@ msgstr ""
msgid "Formats"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:68
#: lib/music_library_web/live/collection_live/show.html.heex:89
#: lib/music_library_web/live/collection_live/show.html.heex:156
#: lib/music_library_web/live/wishlist_live/show.html.heex:68
#: lib/music_library_web/live/wishlist_live/show.html.heex:90
#, elixir-autogen, elixir-format
@@ -121,7 +120,7 @@ msgstr ""
msgid "Import"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:148
#: lib/music_library_web/live/collection_live/show.html.heex:215
#: lib/music_library_web/live/wishlist_live/show.html.heex:141
#, elixir-autogen, elixir-format
msgid "Inserted at"
@@ -148,7 +147,7 @@ msgstr ""
msgid "Logout"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:103
#: lib/music_library_web/live/collection_live/show.html.heex:170
#: lib/music_library_web/live/record_live/form_component.ex:47
#: lib/music_library_web/live/wishlist_live/show.html.heex:104
#, elixir-autogen, elixir-format
@@ -172,6 +171,7 @@ msgid "No results"
msgstr ""
#: lib/music_library_web/components/record_components.ex:104
#: lib/music_library_web/live/collection_live/show.html.heex:38
#, elixir-autogen, elixir-format
msgid "Open options"
msgstr ""
@@ -188,6 +188,7 @@ msgid "Previous"
msgstr ""
#: lib/music_library_web/components/record_components.ex:170
#: lib/music_library_web/live/collection_live/show.html.heex:129
#, elixir-autogen, elixir-format
msgid "Purchase"
msgstr ""
@@ -197,7 +198,7 @@ msgstr ""
msgid "Purchased at"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:125
#: lib/music_library_web/live/collection_live/show.html.heex:192
#, elixir-autogen, elixir-format
msgid "Purchased on"
msgstr ""
@@ -246,7 +247,7 @@ msgid "Search for records"
msgstr ""
#: lib/music_library_web/components/record_components.ex:139
#: lib/music_library_web/live/collection_live/show.ex:145
#: lib/music_library_web/live/collection_live/show.ex:147
#: lib/music_library_web/live/wishlist_live/show.ex:128
#, elixir-autogen, elixir-format
msgid "Show"
@@ -275,7 +276,7 @@ msgid "Success!"
msgstr ""
#: lib/music_library_web/live/collection_live/index.ex:20
#: lib/music_library_web/live/collection_live/show.ex:19
#: lib/music_library_web/live/collection_live/show.ex:21
#: lib/music_library_web/live/wishlist_live/index.ex:20
#: lib/music_library_web/live/wishlist_live/show.ex:19
#, elixir-autogen, elixir-format
@@ -302,7 +303,7 @@ msgstr ""
msgid "Types"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:156
#: lib/music_library_web/live/collection_live/show.html.heex:223
#: lib/music_library_web/live/wishlist_live/show.html.heex:149
#, elixir-autogen, elixir-format
msgid "Updated at"
@@ -342,13 +343,13 @@ msgstr ""
msgid "close"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:113
#: lib/music_library_web/live/collection_live/show.html.heex:180
#: lib/music_library_web/live/wishlist_live/show.html.heex:114
#, elixir-autogen, elixir-format
msgid "Copy MusicBrainz ID to clipboard"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:167
#: lib/music_library_web/live/collection_live/show.html.heex:234
#: lib/music_library_web/live/wishlist_live/show.html.heex:160
#, elixir-autogen, elixir-format
msgid "MusicBrainz data"
@@ -381,19 +382,19 @@ msgstr ""
msgid "Number of included records"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:136
#: lib/music_library_web/live/collection_live/show.html.heex:203
#: lib/music_library_web/live/wishlist_live/show.html.heex:129
#, elixir-autogen, elixir-format
msgid "Includes"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex:61
#: lib/music_library_web/live/collection_live/show.ex:63
#: lib/music_library_web/live/wishlist_live/show.ex:61
#, elixir-autogen, elixir-format
msgid "Error refreshing MusicBrainz data"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex:53
#: lib/music_library_web/live/collection_live/show.ex:55
#: lib/music_library_web/live/wishlist_live/show.ex:53
#, elixir-autogen, elixir-format
msgid "MusicBrainz data refreshed successfully"
@@ -404,26 +405,22 @@ msgstr ""
msgid "Refresh LastFm Feed"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex:93
#: lib/music_library_web/live/collection_live/show.ex:95
#: lib/music_library_web/live/wishlist_live/show.ex:73
#, elixir-autogen, elixir-format
msgid "Cover refreshed successfully"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:44
#: lib/music_library_web/live/wishlist_live/show.html.heex:44
#, elixir-autogen, elixir-format
msgid "Cover"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:56
#: lib/music_library_web/live/wishlist_live/show.html.heex:56
#, elixir-autogen, elixir-format
msgid "MB Data"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:37
#: lib/music_library_web/live/collection_live/show.html.heex:49
#: lib/music_library_web/live/wishlist_live/show.html.heex:37
#: lib/music_library_web/live/wishlist_live/show.html.heex:49
#, elixir-autogen, elixir-format
@@ -435,7 +432,7 @@ msgstr ""
msgid "Error refreshing Cover"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex:101
#: lib/music_library_web/live/collection_live/show.ex:103
#, elixir-autogen, elixir-format
msgid "Error refreshing cover"
msgstr ""
@@ -446,7 +443,7 @@ msgid "No MB ID"
msgstr ""
#: lib/music_library_web/live/artist_live/show.ex:39
#: lib/music_library_web/live/collection_live/show.ex:120
#: lib/music_library_web/live/collection_live/show.ex:122
#, elixir-autogen, elixir-format
msgid "Details"
msgstr ""
@@ -507,20 +504,34 @@ msgstr ""
msgid "There was an error loading the play count"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex:81
#: lib/music_library_web/live/collection_live/show.ex:83
#: lib/music_library_web/live/wishlist_live/show.ex:101
#, elixir-autogen, elixir-format
msgid "Error populating genres"
msgstr ""
#: lib/music_library_web/live/collection_live/show.ex:73
#: lib/music_library_web/live/collection_live/show.ex:75
#: lib/music_library_web/live/wishlist_live/show.ex:93
#, elixir-autogen, elixir-format
msgid "Genres populated successfully"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:61
#: lib/music_library_web/live/wishlist_live/show.html.heex:61
#, elixir-autogen, elixir-format
msgid "Populate"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:118
#, elixir-autogen, elixir-format
msgid "Populate genres"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:102
#, elixir-autogen, elixir-format
msgid "Refresh MB data"
msgstr ""
#: lib/music_library_web/live/collection_live/show.html.heex:86
#, elixir-autogen, elixir-format
msgid "Refresh cover"
msgstr ""