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

feat: dropdown component #483

Merged
merged 16 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ let Hooks = {
StickyScroll: StickyScroll
};


MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
Hooks.ScrollToTop = {
mounted() {
this.el.addEventListener("click", e => {
Expand Down
26 changes: 18 additions & 8 deletions lib/atomic_web/components/calendar/calendar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule AtomicWeb.Components.Calendar do
import AtomicWeb.CalendarUtils
import AtomicWeb.Components.CalendarMonth
import AtomicWeb.Components.CalendarWeek
import AtomicWeb.Components.Dropdown

alias Timex.Duration

Expand Down Expand Up @@ -85,14 +86,23 @@ defmodule AtomicWeb.Components.Calendar do
</div>
<div class="hidden md:ml-4 md:flex md:items-center">
<div class="relative">
<a @click="mode_view = !mode_view" class="flex cursor-pointer items-center py-2 pr-2 pl-3 text-sm font-medium text-zinc-700 hover:bg-zinc-50" id="menu-button" aria-expanded="false" aria-haspopup="true">
<%= if @mode == "month" do
gettext("Month view")
else
gettext("Week view")
end %>
<.icon name={:chevron_down} solid class="ml-2 h-5 w-5 text-zinc-400" />
</a>
<.dropdown
orientation={:down}
id="calendar-dropdown"
items={[
%{name: gettext("Week view"), link: @current_week_path},
%{name: gettext("Month view"), link: @current_month_path}
]}
>
<.button color={:white}>
<%= if @mode == "month" do %>
<%= gettext("Month view") %>
<% else %>
<%= gettext("Week view") %>
<% end %>
<.icon name={:chevron_down} solid class="-mr-1 ml-2 h-5 w-5" />
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
</.button>
</.dropdown>

<div x-bind:class="mode_view ?'block' : 'hidden'" class="absolute right-0 mt-3 w-36 origin-top-right overflow-hidden rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
<div class="py-1" role="none">
Expand Down
33 changes: 33 additions & 0 deletions lib/atomic_web/components/dropdown.ex
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule AtomicWeb.Components.Dropdown do
@moduledoc false
use AtomicWeb, :component
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved

alias Phoenix.LiveView.JS

attr :id, :string, required: true, doc: "The id of the dropdown."
attr :items, :list, default: [], doc: "The items to display in the dropdown."

attr :orientation, :atom,
required: true,
doc: "The orientation of the dropdown.",
values: [:down, :up]

slot :inner_block, required: true, doc: "Slot for the body content of the dropdown."

def dropdown(assigns) do
~H"""
<div class="relative inline-block text-left" phx-click={JS.toggle(to: "##{assigns.id}", in: "block", out: "hidden")} phx-click-away={JS.hide(to: "##{assigns.id}")}>
<%= render_slot(@inner_block) %>
<div id={assigns.id} class={"#{if assigns.orientation == :down, do: "origin-top-right", else: "origin-bottom-right bottom-full"} absolute right-0 mt-2 hidden w-56 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5"}>
<div class="py-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
<%= for item <- assigns.items do %>
<a href={item.link} class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900" role="menuitem">
<%= item.name %>
</a>
<% end %>
</div>
</div>
</div>
"""
end
end
2 changes: 1 addition & 1 deletion lib/atomic_web/live/membership_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule AtomicWeb.MembershipLive.Index do
import AtomicWeb.Helpers
import AtomicWeb.Components.Pagination
import AtomicWeb.Components.Table

import AtomicWeb.Components.Dropdown
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
alias Atomic.Organizations

@impl true
Expand Down
20 changes: 10 additions & 10 deletions lib/atomic_web/live/membership_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@
<div x-data="{ menu: false }" class="flex w-100 flex-row justify-end">
<!-- Export option dropdown -->
<div class="relative block font-semibold">
<button
type="button"
class="group flex border-2 rounded-md bg-white border-orange-500 py-2 px-3.5 text-sm font-medium text-orange-500 shadow-sm hover:bg-orange-600 hover:text-white"
<.dropdown
orientation={:down}
id="export-menu-button"
aria-expanded="false"
aria-haspopup="true"
@click="menu = ! menu"
@click.away="menu = false"
@keydown.escape="menu = false"
items={[
%{name: "Export to CSV", link: Routes.data_export_path(@socket, :export_memberships_csv, @params["organization_id"])},
%{name: "Export to Excel", link: Routes.data_export_path(@socket, :export_memberships_xlsx, @params["organization_id"])}
]}
>
<.icon name={:cloud_arrow_down} solid class="mr-2 -ml-1 w-5 h-5 text-orange-500 group-hover:text-white" /> Export Memberships
</button>
<.button color={:primary} variant={:inverted} class="group">
<.icon name={:cloud_arrow_down} solid class="mr-2 -ml-1 w-5 h-5 text-orange-500 group-hover:text-white" /> Export Memberships
</.button>
</.dropdown>
<div
class="absolute right-0 z-10 mt-2.5 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
role="menu"
Expand Down
128 changes: 54 additions & 74 deletions lib/atomic_web/templates/layout/_live_navbar.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -126,46 +126,35 @@
<!-- Profile menu or "Sign in" -->
<li class="-mx-6 mt-auto -mb-2" @click="menu = ! menu">
<span class="sr-only">Open user menu</span>
<button class="flex items-center gap-x-4 px-6 py-3 text-sm font-semibold leading-6 text-zinc-700 select-none">
<AtomicWeb.Components.Avatar.avatar
name={@current_user.name}
src={
if @current_user.profile_picture do
Uploaders.ProfilePicture.url({@current_user, @current_user.profile_picture}, :original)
else
nil
end
}
size={:xs}
color={:light_gray}
class="!text-sm"
/>
<span class="hidden lg:flex lg:items-center">
<span class="text-sm font-semibold leading-6 text-zinc-900" aria-hidden="true"><%= @current_user.name %></span>
<svg class="-rotate-90 ml-2 h-5 w-5 text-zinc-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
</svg>
</span>
<div
class="absolute right-0 z-10 w-32 mb-16 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu-button"
tabindex="-1"
x-show="menu"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
@click.away="menu = false"
@keydown.escape="menu = false"
>
<%= link("Your profile", to: Routes.profile_show_path(@socket, :show, @current_user), class: "block px-3 py-1 text-sm leading-6 text-zinc-900 hover:bg-zinc-50") %>
<%= link("Sign out", to: Routes.user_session_path(@socket, :delete), method: :delete, class: "block px-3 py-1 text-sm leading-6 text-zinc-900 hover:bg-zinc-50") %>
</div>
</button>
<AtomicWeb.Components.Dropdown.dropdown
orientation={:top}
items={[
%{name: "Your profile", link: Routes.profile_show_path(@socket, :show, @current_user)},
%{name: "Sign out", link: Routes.user_session_path(@socket, :delete)}
]}
id="user-menu-button"
>
<button class="flex items-center gap-x-4 px-6 py-3 text-sm font-semibold leading-6 text-zinc-700 select-none">
<AtomicWeb.Components.Avatar.avatar
name={@current_user.name}
src={
if @current_user.profile_picture do
Uploaders.ProfilePicture.url({@current_user, @current_user.profile_picture}, :original)
else
nil
end
}
size={:xs}
fg_color="zinc-400"
bg_color="zinc-200"
class="!text-sm"
/>
<span class="hidden lg:flex lg:items-center">
<span class="text-sm font-semibold leading-6 text-zinc-900" aria-hidden="true"><%= @current_user.name %></span>
<.icon name={:chevron_right} solid class="h-5 w-5 text-zinc-400" />
</span>
</button>
</AtomicWeb.Components.Dropdown.dropdown>
</li>
<% else %>
<div class="border-b border-zinc-200 mt-auto"></div>
Expand All @@ -192,41 +181,32 @@
<%= if @is_authenticated? do %>
<!-- Profile menu or "Sign in" -->
<div class="relative block lg:hidden font-semibold">
<button type="button" class="-m-1.5 flex items-center p-1.5" id="user-menu-button" aria-expanded="false" aria-haspopup="true" @click="menu = ! menu" @click.away="menu = false" @keydown.escape="menu = false">
<span class="sr-only">Open user menu</span>
<AtomicWeb.Components.Avatar.avatar
name={@current_user.name}
src={
if @current_user.profile_picture do
Uploaders.ProfilePicture.url({@current_user, @current_user.profile_picture}, :original)
else
nil
end
}
size={:xs}
color={:light_gray}
class="!text-[16px]"
/>
</button>
<div
class="absolute right-0 z-10 mt-2.5 w-32 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu-button"
tabindex="-1"
x-show="menu"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
@click.away="menu = false"
@keydown.escape="menu = false"
<AtomicWeb.Components.Dropdown.dropdown
orientation={:down}
items={[
%{name: "Your profile", link: Routes.profile_show_path(@socket, :show, @current_user)},
%{name: "Sign out", link: Routes.user_session_path(@socket, :delete)}
]}
id="user-dropdown-menu"
>
<%= link("Your profile", to: Routes.profile_show_path(@socket, :show, @current_user), class: "block px-3 py-1 text-sm leading-6 text-zinc-900 hover:bg-zinc-50") %>
<%= link("Sign out", to: Routes.user_session_path(@socket, :delete), method: :delete, class: "block px-3 py-1 text-sm leading-6 text-zinc-900 hover:bg-zinc-50") %>
</div>
<button type="button" class="-m-1.5 flex items-center p-1.5" id="user-menu-button" aria-expanded="false" aria-haspopup="true" @click="menu = ! menu" @click.away="menu = false" @keydown.escape="menu = false">
<span class="sr-only">Open user menu</span>
<AtomicWeb.Components.Avatar.avatar
name={@current_user.name}
src={
if @current_user.profile_picture do
Uploaders.ProfilePicture.url({@current_user, @current_user.profile_picture}, :original)
else
nil
end
}
size={:xs}
fg_color="zinc-400"
bg_color="zinc-200"
class="!text-[16px]"
/>
</button>
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
</AtomicWeb.Components.Dropdown.dropdown>
</div>
<% else %>
<div class="flex lg:hidden flex-1 justify-end">
Expand Down
23 changes: 23 additions & 0 deletions storybook/components/dropdown.story.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule AtomicWeb.Storybook.Components.Dropdown do
use PhoenixStorybook.Story, :component

alias AtomicWeb.Components.Dropdown

def function, do: &Dropdown.dropdown/1

def variations do
[
%Variation{
id: :default,
attributes: %{
label: "Dropdown",
items: [
%{name: "Profile", link: "#"},
%{name: "Settings", link: "#"},
%{name: "Logout", link: "#"}
]
}
}
]
end
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
end
Loading