mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-25 01:10:59 +00:00
Add dropdown component
This commit is contained in:
parent
81b86c2c21
commit
7209a3121c
3 changed files with 95 additions and 68 deletions
|
@ -57,7 +57,7 @@ Hooks.Menu = {
|
||||||
window.requestAnimationFrame(() => this.activate(0))
|
window.requestAnimationFrame(() => this.activate(0))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.menuItemsContainer.addEventListener("phx:hide", () => this.reset())
|
this.menuItemsContainer.addEventListener("phx:hide-start", () => this.reset())
|
||||||
},
|
},
|
||||||
activate(index, fallbackIndex){
|
activate(index, fallbackIndex){
|
||||||
let menuItems = this.menuItems()
|
let menuItems = this.menuItems()
|
||||||
|
|
|
@ -158,6 +158,88 @@ defmodule LiveBeatsWeb.LiveHelpers do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a button triggered dropdown with aria keyboard and focus supporrt.
|
||||||
|
|
||||||
|
Accepts the follow slots:
|
||||||
|
|
||||||
|
* `:id` - The id to uniquely identify this dropdown
|
||||||
|
* `:img` - The optional img to show beside the button title
|
||||||
|
* `:title` - The button title
|
||||||
|
* `:subtitle` - The button subtitle
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<.dropdown id={@id}>
|
||||||
|
<:img src={@current_user.avatar_url}/>
|
||||||
|
<:title><%= @current_user.name %></:title>
|
||||||
|
<:subtitle>@<%= @current_user.username %></:subtitle>
|
||||||
|
|
||||||
|
<:link redirect_to={profile_path(@current_user)}>View Profile</:link>
|
||||||
|
<:link redirect_to={Routes.settings_path(LiveBeatsWeb.Endpoint, :edit)}Settings</:link>
|
||||||
|
</.dropdown>
|
||||||
|
"""
|
||||||
|
def dropdown(assigns) do
|
||||||
|
assigns =
|
||||||
|
assigns
|
||||||
|
|> assign_new(:img, fn -> nil end)
|
||||||
|
|> assign_new(:title, fn -> nil end)
|
||||||
|
|> assign_new(:subtitle, fn -> nil end)
|
||||||
|
|
||||||
|
~H"""
|
||||||
|
<!-- User account dropdown -->
|
||||||
|
<div class="px-3 mt-6 relative inline-block text-left">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
id={@id}
|
||||||
|
type="button"
|
||||||
|
class="group w-full bg-gray-100 rounded-md px-3.5 py-2 text-sm text-left font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
|
||||||
|
phx-click={show_dropdown("##{@id}-dropdown")}
|
||||||
|
phx-hook="Menu"
|
||||||
|
data-active-class="bg-gray-100"
|
||||||
|
aria-haspopup="true"
|
||||||
|
>
|
||||||
|
<span class="flex w-full justify-between items-center">
|
||||||
|
<span class="flex min-w-0 items-center justify-between space-x-3">
|
||||||
|
<%= for img <- @img do %>
|
||||||
|
<img class="w-10 h-10 bg-gray-300 rounded-full flex-shrink-0" alt="" {assigns_to_attributes(img)}/>
|
||||||
|
<% end %>
|
||||||
|
<span class="flex-1 flex flex-col min-w-0">
|
||||||
|
<span class="text-gray-900 text-sm font-medium truncate"><%= render_slot(@title) %></span>
|
||||||
|
<span class="text-gray-500 text-sm truncate"><%= render_slot(@subtitle) %></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
|
fill="currentColor" aria-hidden="true">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id={"#{@id}-dropdown"}
|
||||||
|
phx-click-away={hide_dropdown("##{@id}-dropdown")}
|
||||||
|
class="hidden z-10 mx-3 origin-top absolute right-0 left-0 mt-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-200"
|
||||||
|
role="menu"
|
||||||
|
aria-labelledby={@id}
|
||||||
|
>
|
||||||
|
<div class="py-1" role="none">
|
||||||
|
<%= for link <- @link do %>
|
||||||
|
<.link
|
||||||
|
tabindex="-1"
|
||||||
|
role="menuitem"
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500" {link}
|
||||||
|
><%= render_slot(link) %></.link>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
def show_mobile_sidebar(js \\ %JS{}) do
|
def show_mobile_sidebar(js \\ %JS{}) do
|
||||||
js
|
js
|
||||||
|> JS.show(to: "#mobile-sidebar-container", transition: "fade-in")
|
|> JS.show(to: "#mobile-sidebar-container", transition: "fade-in")
|
||||||
|
|
|
@ -5,6 +5,8 @@ defmodule LiveBeatsWeb.LayoutView do
|
||||||
# so we instruct Elixir to not warn if the dashboard route is missing.
|
# so we instruct Elixir to not warn if the dashboard route is missing.
|
||||||
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
|
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
|
||||||
|
|
||||||
|
alias LiveBeatsWeb.Endpoint
|
||||||
|
|
||||||
def sidebar_active_users(assigns) do
|
def sidebar_active_users(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
|
@ -40,14 +42,14 @@ defmodule LiveBeatsWeb.LayoutView do
|
||||||
</.link>
|
</.link>
|
||||||
|
|
||||||
<.link
|
<.link
|
||||||
redirect_to={Routes.settings_path(LiveBeatsWeb.Endpoint, :edit)}
|
redirect_to={Routes.settings_path(Endpoint, :edit)}
|
||||||
class={"text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md #{if @active_tab == :settings, do: "bg-gray-200 hover:bg-gray-200"}"}
|
class={"text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md #{if @active_tab == :settings, do: "bg-gray-200 hover:bg-gray-200"}"}
|
||||||
>
|
>
|
||||||
<.icon name={:adjustments} outlined class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"/>
|
<.icon name={:adjustments} outlined class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"/>
|
||||||
Settings
|
Settings
|
||||||
</.link>
|
</.link>
|
||||||
<% else %>
|
<% else %>
|
||||||
<.link redirect_to={Routes.sign_in_path(LiveBeatsWeb.Endpoint, :index)}
|
<.link redirect_to={Routes.sign_in_path(Endpoint, :index)}
|
||||||
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
|
class="text-gray-700 hover:text-gray-900 hover:bg-gray-50 group flex items-center px-2 py-2 text-sm font-medium rounded-md"
|
||||||
>
|
>
|
||||||
<svg class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"
|
<svg class="text-gray-400 group-hover:text-gray-500 mr-3 flex-shrink-0 h-6 w-6"
|
||||||
|
@ -65,72 +67,15 @@ defmodule LiveBeatsWeb.LayoutView do
|
||||||
|
|
||||||
def sidebar_account_dropdown(assigns) do
|
def sidebar_account_dropdown(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<!-- User account dropdown -->
|
<.dropdown id={@id}>
|
||||||
<div class="px-3 mt-6 relative inline-block text-left">
|
<:img src={@current_user.avatar_url}/>
|
||||||
<div>
|
<:title><%= @current_user.name %></:title>
|
||||||
<button
|
<:subtitle>@<%= @current_user.username %></:subtitle>
|
||||||
id={"#{@id}-menu"}
|
|
||||||
type="button"
|
|
||||||
class="group w-full bg-gray-100 rounded-md px-3.5 py-2 text-sm text-left font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
|
|
||||||
phx-click={show_dropdown("##{@id}-dropdown")}
|
|
||||||
phx-hook="Menu"
|
|
||||||
data-active-class="bg-gray-100"
|
|
||||||
aria-haspopup="true"
|
|
||||||
>
|
|
||||||
<span class="flex w-full justify-between items-center">
|
|
||||||
<span class="flex min-w-0 items-center justify-between space-x-3">
|
|
||||||
<img class="w-10 h-10 bg-gray-300 rounded-full flex-shrink-0"
|
|
||||||
src={@current_user.avatar_url}
|
|
||||||
alt="">
|
|
||||||
<span class="flex-1 flex flex-col min-w-0">
|
|
||||||
<span class="text-gray-900 text-sm font-medium truncate"><%= @current_user.name %></span>
|
|
||||||
<span class="text-gray-500 text-sm truncate">@<%= @current_user.username %></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
|
||||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
|
||||||
fill="currentColor" aria-hidden="true">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id={"#{@id}-dropdown"}
|
|
||||||
phx-click-away={hide_dropdown("##{@id}-dropdown")}
|
|
||||||
class="hidden z-10 mx-3 origin-top absolute right-0 left-0 mt-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-200"
|
|
||||||
role="menu"
|
|
||||||
aria-labelledby={"#{@id}-menu"}
|
|
||||||
phx-update="ignore"
|
|
||||||
>
|
|
||||||
<div class="py-1" role="none">
|
|
||||||
<.link
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
redirect_to={profile_path(@current_user)}
|
|
||||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
|
|
||||||
>View Profile</.link>
|
|
||||||
<.link
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
redirect_to={Routes.settings_path(LiveBeatsWeb.Endpoint, :edit)}
|
|
||||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
|
|
||||||
>Settings</.link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="py-1" role="none">
|
<:link redirect_to={profile_path(@current_user)}>View Profile</:link>
|
||||||
<.link
|
<:link redirect_to={Routes.settings_path(Endpoint, :edit)}>Settings</:link>
|
||||||
role="menuitem"
|
<:link href={Routes.o_auth_callback_path(Endpoint, :sign_out)} method={:delete}>Sign out</:link>
|
||||||
tabindex="-1"
|
</.dropdown>
|
||||||
href={Routes.o_auth_callback_path(LiveBeatsWeb.Endpoint, :sign_out)}
|
|
||||||
method={:delete}
|
|
||||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-purple-500"
|
|
||||||
>Sign out</.link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue