Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate dropdowns #4934

Merged
merged 12 commits into from
Jan 7, 2025
1 change: 1 addition & 0 deletions assets/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ module.exports = {
plugin(({ addVariant }) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
plugin(({ addVariant }) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
plugin(({ addVariant }) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])),
plugin(({ addVariant }) => addVariant("ui-disabled", ["&[data-ui-state~=\"disabled\"]", ":where([data-ui-state~=\"disabled\"]) &"])),
]
}
75 changes: 54 additions & 21 deletions lib/plausible_web/components/generic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -221,26 +221,29 @@ defmodule PlausibleWeb.Components.Generic do
"""
end

attr :class, :string, default: ""

slot :button, required: true do
attr(:class, :string)
end

slot :panel, required: true do
slot :menu, required: true do
attr(:class, :string)
end

def dropdown(assigns) do
assigns = assign(assigns, :menu_class, assigns.menu |> List.first() |> Map.get(:class, ""))

~H"""
<div
x-data="dropdown"
x-on:keydown.escape.prevent.stop="close($refs.button)"
x-on:focusin.window="! $refs.panel.contains($event.target) && close()"
class="relative inline-block text-left"
>
<button x-ref="button" x-on:click="toggle()" type="button" class={List.first(@button).class}>
<%= render_slot(List.first(@button)) %>
</button>
<div
x-ref="panel"
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 scale-95"
Expand All @@ -250,36 +253,64 @@ defmodule PlausibleWeb.Components.Generic do
x-transition:leave-end="opacity-0 scale-95"
x-on:click.outside="close($refs.button)"
style="display: none;"
class={List.first(@panel).class}
class={[
"origin-top-right absolute z-50 right-0 mt-2 p-1 w-max rounded-md shadow-lg overflow-hidden bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 focus:outline-none",
@menu_class
]}
>
<%= render_slot(List.first(@panel)) %>
<%= render_slot(List.first(@menu)) %>
</div>
</div>
"""
end

attr(:href, :string, required: true)
attr(:href, :string)
attr(:class, :string, default: "")
attr(:new_tab, :boolean, default: false)
attr(:rest, :global)
attr(:disabled, :boolean, default: false)
attr(:rest, :global, include: ~w(method))
slot(:inner_block, required: true)

def dropdown_link(assigns) do
class =
"w-full inline-flex text-gray-700 dark:text-gray-300 px-3.5 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-100"

class =
if assigns.new_tab do
"#{class} justify-between"
@base_class "block rounded-lg text-sm/6 text-gray-900 ui-disabled:text-gray-500 dark:text-gray-100 dark:ui-disabled:text-gray-400 px-3.5 py-1.5"
@clickable_class "hover:bg-gray-100 dark:hover:bg-gray-700"
def dropdown_item(assigns) do
assigns =
if assigns[:disabled] do
assign(assigns, :state, "disabled")
else
class
assign(assigns, :state, "")
end

assigns = assign(assigns, :class, class)
if assigns[:href] && !assigns[:disabled] do
assigns = assign(assigns, :class, [assigns[:class], @base_class, @clickable_class])

~H"""
<.unstyled_link
class={@class}
new_tab={@new_tab}
href={@href}
x-on:click="close()"
data-ui-state={@state}
{@rest}
>
<%= render_slot(@inner_block) %>
</.unstyled_link>
"""
else
assigns = assign(assigns, :class, [assigns[:class], @base_class])

~H"""
<div data-ui-state={@state} class={@class}>
<%= render_slot(@inner_block) %>
</div>
"""
end
end

def dropdown_divider(assigns) do
~H"""
<.unstyled_link new_tab={@new_tab} href={@href} x-on:click="close()" class={@class} {@rest}>
<%= render_slot(@inner_block) %>
</.unstyled_link>
<div class="mx-3.5 my-1 h-px border-0 bg-gray-950/5 sm:mx-3 dark:bg-white/10" role="separator">
</div>
"""
end

Expand Down Expand Up @@ -453,8 +484,10 @@ defmodule PlausibleWeb.Components.Generic do
attr(:id, :string, default: "shuttle")

defp icon_class(link_assigns) do
if String.contains?(link_assigns[:class], "text-sm") or
String.contains?(link_assigns[:class], "text-xs") do
classes = List.wrap(link_assigns[:class]) |> Enum.join(" ")

if String.contains?(classes, "text-sm") or
String.contains?(classes, "text-xs") do
["w-3 h-3"]
else
["w-4 h-4"]
Expand Down
75 changes: 39 additions & 36 deletions lib/plausible_web/live/sites.ex
Original file line number Diff line number Diff line change
Expand Up @@ -220,51 +220,54 @@ defmodule PlausibleWeb.Live.Sites do
</div>
</.unstyled_link>

<.ellipsis_menu site={@site} />
<div class="absolute right-0 top-2">
<.ellipsis_menu site={@site} />
</div>
</li>
"""
end

def ellipsis_menu(assigns) do
~H"""
<.dropdown>
<:button class="absolute top-0 right-0 h-10 w-10 rounded-md hover:cursor-pointer text-gray-400 dark:text-gray-600 hover:text-black dark:hover:text-indigo-400">
<Heroicons.ellipsis_vertical class="absolute top-3 right-3 w-4 h-4" />
<:button class="size-10 rounded-md hover:cursor-pointer text-gray-400 dark:text-gray-600 hover:text-black dark:hover:text-indigo-400">
<Heroicons.ellipsis_vertical class="absolute top-3 right-3 size-4" />
</:button>
<:panel class="absolute top-7 right-3 z-10 mt-2 w-40 rounded-md bg-white dark:bg-gray-900 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div class="py-1 text-sm" role="none">
<.dropdown_link
:if={List.first(@site.memberships).role != :viewer}
href={"/#{URI.encode_www_form(@site.domain)}/settings/general"}
>
<Heroicons.cog_6_tooth class="mr-3 h-5 w-5" />
<span>Settings</span>
</.dropdown_link>

<.dropdown_link
href="#"
x-on:click.prevent
phx-click={
JS.hide(
transition: {"duration-500", "opacity-100", "opacity-0"},
to: "#site-card-#{hash_domain(@site.domain)}",
time: 500
)
|> JS.push("pin-toggle")
}
phx-value-domain={@site.domain}
>
<.icon_pin
:if={@site.pinned_at}
class="pt-1 mr-3 h-5 w-5 text-red-400 stroke-red-500 dark:text-yellow-600 dark:stroke-yellow-700"
/>
<span :if={@site.pinned_at}>Unpin Site</span>
<:menu class="!mt-0 mr-4 min-w-40">
<!-- adjust position because click area is much bigger than icon. Default positioning from click area looks weird -->
<.dropdown_item
:if={List.first(@site.memberships).role != :viewer}
href={"/#{URI.encode_www_form(@site.domain)}/settings/general"}
class="!flex items-center gap-x-2"
>
<Heroicons.cog_6_tooth class="size-4" />
<span>Settings</span>
</.dropdown_item>

<.dropdown_item
href="#"
x-on:click.prevent
phx-click={
JS.hide(
transition: {"duration-500", "opacity-100", "opacity-0"},
to: "#site-card-#{hash_domain(@site.domain)}",
time: 500
)
|> JS.push("pin-toggle")
}
phx-value-domain={@site.domain}
class="!flex items-center gap-x-2"
>
<.icon_pin
:if={@site.pinned_at}
class="size-4 text-red-400 stroke-red-500 dark:text-yellow-600 dark:stroke-yellow-700"
/>
<span :if={@site.pinned_at}>Unpin Site</span>

<.icon_pin :if={[email protected]_at} class="pt-1 mr-3 h-5 w-5" />
<span :if={[email protected]_at}>Pin Site</span>
</.dropdown_link>
</div>
</:panel>
<.icon_pin :if={[email protected]_at} class="size-4" />
<span :if={[email protected]_at}>Pin Site</span>
</.dropdown_item>
</:menu>
</.dropdown>
"""
end
Expand Down
72 changes: 42 additions & 30 deletions lib/plausible_web/templates/layout/_header.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -65,39 +65,51 @@
class="w-7 rounded-full"
/>
</:button>
<:panel class="absolute right-0 z-10 mt-2 w-60 origin-top-right divide-y divide-gray-100 dark:divide-gray-400 rounded-md bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div class="px-3.5 py-3" role="none">
<p class="block text-sm text-gray-500 dark:text-gray-400" role="none">
Signed in as
</p>
<:menu>
<.dropdown_item>
<div class="text-xs text-gray-500 dark:text-gray-400">Signed in as</div>
<p class="truncate font-medium text-gray-900 dark:text-gray-100" role="none">
<%= @conn.assigns[:current_user].email %>
</p>
</div>
<div class="py-1.5" role="none">
<.dropdown_link href={Routes.settings_path(@conn, :index)}>
Account Settings
</.dropdown_link>
<.dropdown_link new_tab href="https://plausible.io/docs">
Help Center
</.dropdown_link>
<%= if ee?() do %>
<.dropdown_link new_tab href="https://plausible.io/contact">
Contact Support
</.dropdown_link>
<.dropdown_link new_tab href={feedback_link(@conn.assigns[:current_user])}>
Feature Requests
</.dropdown_link>
<% else %>
<.dropdown_link new_tab href="https://github.com/plausible/analytics">
Github Repo
</.dropdown_link>
<% end %>
</div>
<div class="py-1.5" role="none">
<.dropdown_link href="/logout">Log Out</.dropdown_link>
</div>
</:panel>
</.dropdown_item>
<.dropdown_divider />
<.dropdown_item href={Routes.settings_path(@conn, :index)}>
Account Settings
</.dropdown_item>
<.dropdown_item
class="!flex justify-between gap-x-12"
new_tab
href="https://plausible.io/docs"
>
Help Center
</.dropdown_item>
<.dropdown_item
:if={ee?()}
class="!flex justify-between gap-x-12"
new_tab
href="https://plausible.io/contact"
>
Contact Support
</.dropdown_item>
<.dropdown_item
:if={ee?()}
class="!flex justify-between gap-x-12"
new_tab
href={feedback_link(@conn.assigns[:current_user])}
>
Feature Requests
</.dropdown_item>
<.dropdown_item
:if={ce?()}
class="!flex justify-between gap-x-12"
new_tab
href="https://github.com/plausible/analytics"
>
Github Repo
</.dropdown_item>
<.dropdown_divider />
<.dropdown_item href="/logout">Log Out</.dropdown_item>
</:menu>
</.dropdown>
</li>
<%= if @conn.assigns[:current_user] && ee?() do %>
Expand Down
Loading
Loading