-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsidebar.ex
202 lines (183 loc) · 7.85 KB
/
sidebar.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
defmodule AtomicWeb.Components.Sidebar do
@moduledoc false
use AtomicWeb, :component
alias Phoenix.LiveView.JS
import AtomicWeb.Components.Icon
alias Atomic.Organizations
import AtomicWeb.Components.Avatar
attr :current_user, :map, required: true
attr :current_organization, :map, required: true
attr :current_page, :atom, required: true
attr :is_authenticated, :boolean, required: true
def desktop_sidebar(assigns) do
user = assigns[:current_user]
organizations = get_organizations(user)
assigns = assign(assigns, :organizations, organizations)
~H"""
<div class="relative z-50 hidden" role="dialog">
<.sidebar_header />
<.sidebar_list current_user={@current_user} current_organization={@current_organization} current_page={@current_page} />
</div>
<!-- Navigation -->
<.navigation current_user={@current_user} current_organization={@current_organization} current_page={@current_page} is_authenticated={@is_authenticated} />
"""
end
def mobile_sidebar(assigns) do
user = assigns[:current_user]
organizations = get_organizations(user)
assigns = assign(assigns, :organizations, organizations)
~H"""
<div class="relative z-50 lg:hidden">
<div class="flex flex-row items-center justify-between">
<button type="button" class="-m-2.5 pl-6 text-zinc-700 lg:hidden" phx-click={show_mobile_sidebar()}>
<span class="sr-only">Open sidebar</span>
<.icon name="hero-bars-3" class="!h-6 !w-6" />
</button>
<div class="left-1/4 mt-auto -mb-2">
<span class="sr-only">Open user menu</span>
<.sidebar_dropdown current_user={@current_user} orientation={:down} />
</div>
</div>
<div id="sidebar-overlay" class="fixed inset-0 z-40 hidden cursor-pointer bg-black bg-opacity-50" phx-click={hide_mobile_sidebar()}></div>
<!-- Sidebar Panel -->
<div id="mobile-sidebar" class="fixed inset-0 z-50 hidden w-64" role="dialog" aria-modal="true">
<div class="fixed inset-0 flex w-fit">
<div class="relative flex w-64 max-w-xs flex-col border-r bg-white">
<div class="flex justify-between p-4">
<.sidebar_header />
<button type="button" phx-click={hide_mobile_sidebar()} class="absolute top-0 right-0 p-4">
<span class="sr-only">Close sidebar</span>
<.icon name="hero-x-mark" class="size-6 text-zinc-700" />
</button>
</div>
<div class="flex-1 overflow-y-auto px-4 py-4">
<.sidebar_list current_user={@current_user} current_organization={@current_organization} current_page={@current_page} />
<%= if Enum.count(@organizations) > 0 do %>
<div class="text-xs font-semibold leading-6 text-zinc-400"><%= gettext("Your organizations") %></div>
<.live_component id="mobile-organizations" module={AtomicWeb.Components.Organizations} current_user={@current_user} current_organization={@current_organization} organizations={@organizations} />
<% end %>
</div>
</div>
</div>
</div>
</div>
"""
end
defp navigation(assigns) do
user = assigns[:current_user]
organizations = get_organizations(user)
assigns = assign(assigns, :organizations, organizations)
~H"""
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col">
<div class="flex grow flex-col gap-y-5 overflow-y-auto border-r border-zinc-200 bg-white px-6 pb-4">
<.sidebar_header />
<.sidebar_list current_user={@current_user} current_organization={@current_organization} current_page={@current_page} />
<!-- Organizations listing -->
<%= if Enum.count(@organizations) > 0 do %>
<div class="text-xs font-semibold leading-6 text-zinc-400"><%= gettext("Your organizations") %></div>
<.live_component id="desktop-organizations" module={AtomicWeb.Components.Organizations} current_user={@current_user} current_organization={@current_organization} organizations={@organizations} />
<% end %>
<!-- Sidebar -->
<div class="absolute bottom-0 w-full">
<.sidebar_dropdown current_user={@current_user} orientation={:up} />
</div>
</div>
</div>
"""
end
defp sidebar_list(assigns) do
~H"""
<ul role="list" class="-mx-2 space-y-1">
<%= for page <- AtomicWeb.Config.pages(@current_user, @current_organization) do %>
<li class="select-none">
<.link navigate={page.url} class={"#{if @current_page == page.key do "bg-zinc-50 text-orange-500" else "text-zinc-700 hover:text-orange-500 hover:bg-zinc-50" end} group flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6"}>
<.icon name={page.icon} class={
"#{if @current_page == page.key do
"text-orange-500"
else
"text-zinc-400 group-hover:text-orange-500"
end} size-6 shrink-0"
} />
<%= page.title %>
</.link>
</li>
<% end %>
</ul>
"""
end
defp sidebar_dropdown(assigns) do
~H"""
<%= if @current_user do %>
<AtomicWeb.Components.Dropdown.dropdown orientation={@orientation} items={dropdown_items(@current_user)} id="user-menu-button">
<:wrapper>
<button class="flex w-full select-none flex-row items-center gap-x-2 px-4 py-3 text-sm font-semibold leading-6 text-zinc-700 lg:px-0">
<AtomicWeb.Components.Avatar.avatar name={@current_user.name} src={user_image(@current_user)} size={:xs} color={:light_gray} class="!text-sm" />
<span class="text-sm font-semibold leading-6"><%= @current_user.name %></span>
<.icon name="hero-chevron-right-solid" class="size-5" />
</button>
</:wrapper>
</AtomicWeb.Components.Dropdown.dropdown>
<% else %>
<.link navigate={~p"/users/log_in"} class="flex select-none items-center space-x-2 px-4 py-3 text-sm font-semibold leading-6 text-zinc-700 lg:px-0">
<span class="text-sm font-semibold leading-6">Log in</span>
<.icon name="hero-arrow-right-end-on-rectangle-solid" class="size-5" />
</.link>
<% end %>
"""
end
defp sidebar_header(assigns) do
~H"""
<.link navigate={~p"/"} class="flex h-16 shrink-0 select-none items-center gap-x-4 pt-4">
<img src={~p"/images/atomic.svg"} class="h-14 w-auto" />
<p class="text-2xl font-semibold text-zinc-400">Atomic</p>
</.link>
"""
end
defp dropdown_items(nil), do: []
defp dropdown_items(current_user), do: authenticated_dropdown_items(current_user)
defp authenticated_dropdown_items(current_user) do
[
%{
name: gettext("Your profile"),
navigate: ~p"/profile/#{current_user}"
},
%{
name: gettext("Sign out"),
href: ~p"/users/log_out",
method: "delete"
}
]
end
defp show_mobile_sidebar(js \\ %JS{}) do
js
|> JS.show(
to: "#mobile-sidebar",
transition:
{"transition ease-in-out duration-300 transform", "-translate-x-full", "translate-x-0"}
)
|> JS.show(to: "#sidebar-overlay")
|> JS.dispatch("focus", to: "#mobile-sidebar")
end
defp hide_mobile_sidebar(js \\ %JS{}) do
js
|> JS.hide(
to: "#mobile-sidebar",
transition:
{"ease-out duration-300", "transform translate-x-0", "transform -translate-x-full"}
)
|> JS.hide(
to: "#sidebar-overlay",
transition: {"ease-out duration-300", "opacity-100", "opacity-0"}
)
|> JS.dispatch("focus", to: "#mobile-sidebar")
end
defp user_image(user) do
if user.profile_picture do
Atomic.Uploaders.ProfilePicture.url({user, user.profile_picture}, :original)
else
""
end
end
defp get_organizations(nil), do: []
defp get_organizations(user), do: Organizations.list_user_organizations(user.id)
end