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: popover component #495

Draft
wants to merge 11 commits into
base: develop
Choose a base branch
from
36 changes: 21 additions & 15 deletions lib/atomic_web/components/activity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule AtomicWeb.Components.Activity do
"""
use AtomicWeb, :component

import AtomicWeb.Components.Avatar
import AtomicWeb.Components.{Avatar, Popover}

alias Atomic.Activities.Activity

Expand All @@ -13,22 +13,28 @@ defmodule AtomicWeb.Components.Activity do
def activity(assigns) do
~H"""
<div>
<div class="flex space-x-3">
<div class="flex">
<div class="my-auto flex-shrink-0">
<.avatar name={@activity.organization.name} color={:light_gray} class="!h-10 !w-10" size={:xs} type={:organization} src={Uploaders.Logo.url({@activity.organization.logo, @activity.organization}, :original)} />
<.popover type={:organization} organization={@activity.organization} position={:left}>
<:wrapper>
<.avatar name={@activity.organization.name} color={:light_gray} class="!h-10 !w-10" size={:xs} type={:organization} src={Uploaders.Logo.url({@activity.organization.logo, @activity.organization}, :original)} />
</:wrapper>
</.popover>
</div>
<div class="min-w-0 flex-1">
<object>
<.link navigate={Routes.organization_show_path(AtomicWeb.Endpoint, :show, @activity.organization.id)}>
<span class="text-sm font-medium text-gray-900 hover:underline focus:outline-none">
<%= @activity.organization.name %>
</span>
</.link>
</object>
<p class="text-sm text-gray-500">
<span class="sr-only">Published on</span>
<time><%= relative_datetime(@activity.inserted_at) %></time>
</p>
<div class="ml-3">
<div class="min-w-0 flex-1">
<object>
<.link navigate={Routes.organization_show_path(AtomicWeb.Endpoint, :show, @activity.organization.id)}>
<span class="text-sm font-medium text-gray-900 hover:underline focus:outline-none">
<%= @activity.organization.name %>
</span>
</.link>
</object>
<p class="text-sm text-gray-500">
<span class="sr-only">Published on</span>
<time><%= relative_datetime(@activity.inserted_at) %></time>
</p>
</div>
</div>
</div>
<h2 class="mt-3 text-base font-semibold text-gray-900"><%= @activity.title %></h2>
Expand Down
36 changes: 21 additions & 15 deletions lib/atomic_web/components/announcement.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,35 @@ defmodule AtomicWeb.Components.Announcement do
"""
use AtomicWeb, :component

import AtomicWeb.Components.Avatar
import AtomicWeb.Components.{Avatar, Popover}

attr :announcement, :map, required: true, doc: "The announcement to render."

def announcement(assigns) do
~H"""
<div>
<div class="flex space-x-3">
<div class="flex">
<div class="my-auto flex-shrink-0">
<.avatar name={@announcement.organization.name} color={:light_gray} class="!h-10 !w-10" size={:xs} type={:organization} src={Uploaders.Logo.url({@announcement.organization.logo, @announcement.organization}, :original)} />
<.popover type={:organization} organization={@announcement.organization} position={:bottom}>
<:wrapper>
<.avatar name={@announcement.organization.name} color={:light_gray} class="!h-10 !w-10" size={:xs} type={:organization} src={Uploaders.Logo.url({@announcement.organization.logo, @announcement.organization}, :original)} />
</:wrapper>
</.popover>
</div>
<div class="min-w-0 flex-1">
<object>
<.link navigate={Routes.organization_show_path(AtomicWeb.Endpoint, :show, @announcement.organization.id)} class="hover:underline focus:outline-none">
<p class="text-sm font-medium text-gray-900">
<%= @announcement.organization.name %>
</p>
</.link>
</object>
<p class="text-sm text-gray-500">
<span class="sr-only">Published on</span>
<time><%= relative_datetime(@announcement.inserted_at) %></time>
</p>
<div class="ml-3">
<div class="min-w-0 flex-1">
<object>
<.link navigate={Routes.organization_show_path(AtomicWeb.Endpoint, :show, @announcement.organization.id)} class="hover:underline focus:outline-none">
<p class="text-sm font-medium text-gray-900">
<%= @announcement.organization.name %>
</p>
</.link>
</object>
<p class="text-sm text-gray-500">
<span class="sr-only">Published on</span>
<time><%= relative_datetime(@announcement.inserted_at) %></time>
</p>
</div>
</div>
</div>
<h2 class="mt-3 text-base font-semibold text-gray-900"><%= @announcement.title %></h2>
Expand Down
2 changes: 1 addition & 1 deletion lib/atomic_web/components/avatar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defmodule AtomicWeb.Components.Avatar do
~H"""
<span class={generate_classes(assigns)}>
<%= if @src do %>
<img src={@src} class={"atomic-avatar--#{assigns.type} h-full w-full"} />
<img src={@src} class={"atomic-avatar--#{assigns.type} h-full w-full z-0"} />
<% else %>
<%= extract_initials(@name) %>
<% end %>
Expand Down
111 changes: 111 additions & 0 deletions lib/atomic_web/components/popover.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
defmodule AtomicWeb.Components.Popover do
@moduledoc """
Renders a popover.
"""

use AtomicWeb, :component

import AtomicWeb.Components.Avatar

attr :type, :atom,
values: [:user, :organization, :button],
default: :user,
doc: "The type of entity associated with the avatar."

attr :position, :atom,
values: [:top, :right, :bottom, :left],
required: true,
doc: "The position of the popover."

attr :organization, :map, default: %{}, doc: "The organization to render."
attr :user, :map, default: %{}, doc: "The user to render."
attr :button, :map, default: %{}, doc: "The button to render."

slot :wrapper,
required: true,
doc: "Slot to be rendered as a wrapper of the popover. For example, a avatar."

def popover(assigns) do
~H"""
<div class="group relative h-min">
<%= render_slot(@wrapper) %>
<div class={[
"hidden z-50 group-hover:block transition delay-700 duration-300 ease-in-out",
triangle_class(position: @position)
]}>
<div class={[
"absolute z-50 w-64 bg-slate-50 border border-gray-200 rounded-lg shadow-md",
popover_position(position: @position)
]}>
<%= render_popover(assigns, type: @type) %>
</div>
</div>
</div>
"""
end

def triangle_class(position: :bottom) do
"before:border-l-[10px] before:border-b-[10px] before:border-r-[10px] before:absolute before:mx-3 before:mb-8 before:border-r-transparent before:border-b-gray-200 before:border-l-transparent"
end

def triangle_class(position: :top) do
"before:bottom-full before:border-l-[10px] before:border-t-[10px] before:border-r-[10px] before:absolute before:mx-3 before:mt-8 before:border-r-transparent before:border-b-gray-200 before:border-l-transparent"
end

def triangle_class(position: :left) do
"before:right-full before:bottom-0 before:border-t-[10px] before:border-l-[10px] before:border-b-[10px] before:absolute before:mt-9 before:border-t-transparent before:border-l-gray-200 before:border-b-transparent"
end

def triangle_class(position: :right) do
"before:left-full before:bottom-0 before:border-t-[10px] before:border-r-[10px] before:border-b-[10px] before:absolute before:mt-9 before:border-t-transparent before:border-r-gray-200 before:border-b-transparent"
end

def popover_position(position: :bottom) do
"mt-2"
end

def popover_position(position: :top) do
"bottom-full mb-2"
end

def popover_position(position: :right) do
"left-full mx-2 top-0"
end

def popover_position(position: :left) do
"right-full mx-2 top-0"
end

def render_popover(assigns, type: :organization) do
~H"""
<div class="relative p-3">
<div class="mb-2 mb-4 flex items-center justify-between">
<.avatar name={@organization.name} color={:light_gray} class="!h-10 !w-10" size={:xs} type={:organization} src={Uploaders.Logo.url({@organization.logo, @organization}, :original)} />
</div>
<p class="text-base font-semibold leading-none text-gray-900">
<.link navigate={Routes.organization_show_path(AtomicWeb.Endpoint, :show, @organization.id)}>
<%= @organization.name %>
</.link>
</p>
<p class="text-sm text-gray-500 dark:text-gray-400">
<%= @organization.description %>
</p>
</div>
"""
end

def render_popover(assigns, type: :button) do
~H"""
<div class="relative p-3">
<div class="mb-2 mb-4 items-center justify-between">
<p class="text-base font-semibold leading-none text-gray-900">
<%= @button.name %>
</p>
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400">
<%= @button.description %>
</p>
</div>
</div>
"""
end
end
2 changes: 1 addition & 1 deletion lib/atomic_web/templates/layout/live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div class="flex relative flex-col min-h-screen">
<div class="flex flex-col mx-auto w-full lg:flex-row xl:px-8 max-w-[1380px]">
<!-- Navbar -->
<div class="relative flex-shrink-0 lg:w-72 lg:flex lg:max-w-[400px]">
<div class="relative flex-shrink-0 z-5 lg:w-72 lg:flex lg:max-w-[400px]">
<%= render("_live_navbar.html", assigns) %>
</div>
<!-- Central content -->
Expand Down
108 changes: 108 additions & 0 deletions storybook/components/popover.story.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
defmodule AtomicWeb.Storybook.Components.Popover do
use PhoenixStorybook.Story, :component

alias AtomicWeb.Components.Popover

def function, do: &Popover.popover/1

def variations do
[
%Variation{
id: :default,
attributes: %{
type: :button,
position: :bottom,
button: %{
name: "Button",
description: "This is a button."
}
},
slots: [
"""
<:wrapper>
<button class="bg-blue-500 text-white px-4 py-2 rounded-md">Button</button>
</:wrapper>
"""
]
},
%VariationGroup{
id: :Positon,
description: "Position",
variations: [
%Variation{
id: :bottom,
attributes: %{
type: :button,
position: :bottom,
button: %{
name: "Button",
description: "The popover is positioned at the bottom."
}
},
slots: [
"""
<:wrapper>
<button class="bg-blue-500 text-white px-4 py-2 rounded-md">Button Bottom</button>
</:wrapper>
"""
]
},
%Variation{
id: :top,
attributes: %{
type: :button,
position: :top,
button: %{
name: "Button",
description: "The popover is positioned at the top."
}
},
slots: [
"""
<:wrapper>
<button class="bg-blue-500 text-white px-4 py-2 rounded-md">Button Top</button>
</:wrapper>
"""
]
},
%Variation{
id: :right,
attributes: %{
type: :button,
position: :right,
button: %{
name: "Button",
description: "The popover is positioned at the right."
}
},
slots: [
"""
<:wrapper>
<button class="bg-blue-500 text-white px-4 py-2 rounded-md">Button Right</button>
</:wrapper>
"""
]
},
%Variation{
id: :left,
attributes: %{
type: :button,
position: :left,
button: %{
name: "Button",
description: "The popover is positioned at the left."
}
},
slots: [
"""
<:wrapper>
<button class="bg-blue-500 text-white px-4 py-2 rounded-md">Button Left</button>
</:wrapper>
"""
]
}
]
}
]
end
end
Loading