Skip to content

Commit

Permalink
Merge pull request #12 from bluzky/feature/hover-card
Browse files Browse the repository at this point in the history
Implement new component hover-card
  • Loading branch information
bluzky authored Jun 14, 2024
2 parents ba526f9 + b6cdda4 commit b5ff577
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 92 deletions.
5 changes: 4 additions & 1 deletion lib/salad_ui/avatar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ defmodule SaladUI.Avatar do

def avatar(assigns) do
~H"""
<span class={classes(["relative h-10 w-10 overflow-hidden rounded-full", @class])} {@rest}>
<span
class={classes(["relative h-10 w-10 shrink-0 overflow-hidden rounded-full", @class])}
{@rest}
>
<%= render_slot(@inner_block) %>
</span>
"""
Expand Down
59 changes: 5 additions & 54 deletions lib/salad_ui/dropdown_menu.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,15 @@ defmodule SaladUI.DropdownMenu do
attr(:rest, :global)

def dropdown_menu_content(assigns) do
assigns =
assign(assigns, :variant_class, side_variant(assigns.side, assigns.align))

~H"""
<div
class={[
"z-50 peer-data-[state=open]:animate-in peer-data-[state=closed]:animate-out peer-data-[state=closed]:fade-out-0 peer-data-[state=open]:fade-in-0 peer-data-[state=closed]:zoom-out-95 peer-data-[state=open]:zoom-in-95 peer-data-[side=bottom]:slide-in-from-top-2 peer-data-[side=left]:slide-in-from-right-2 peer-data-[side=right]:slide-in-from-left-2 peer-data-[side=top]:slide-in-from-bottom-2",
"z-50 animate-in peer-data-[state=closed]:fade-out-0 peer-data-[state=open]:fade-in-0 peer-data-[state=closed]:zoom-out-95 peer-data-[state=open]:zoom-in-95 peer-data-[side=bottom]:slide-in-from-top-2 peer-data-[side=left]:slide-in-from-right-2 peer-data-[side=right]:slide-in-from-left-2 peer-data-[side=top]:slide-in-from-bottom-2",
"absolute peer-data-[state=closed]:hidden",
position(@side, @align),
@variant_class,
@class
]}
{@rest}
Expand All @@ -89,58 +92,6 @@ defmodule SaladUI.DropdownMenu do
"""
end

defp position("bottom", align) do
base = "top-full mt-2"

align =
case align do
"start" -> "left-0"
"center" -> "left-1/2 -translate-x-1/2"
"end" -> "right-0"
end

"#{base} #{align}"
end

defp position("top", align) do
base = "bottom-full mb-2"

align =
case align do
"start" -> "left-0"
"center" -> "left-1/2 -translate-x-1/2"
"end" -> "right-0"
end

"#{base} #{align}"
end

defp position("right", align) do
base = "left-full ml-2"

align =
case align do
"start" -> "top-0"
"center" -> "top-1/2 -translate-y-1/2"
"end" -> "bottom-0"
end

"#{base} #{align}"
end

defp position("left", align) do
base = "right-full mr-2"

align =
case align do
"start" -> "top-0"
"center" -> "top-1/2 -translate-y-1/2"
"end" -> "bottom-0"
end

"#{base} #{align}"
end

defp toggle(js \\ %JS{}) do
JS.toggle_attribute(js, {"data-state", "open", "closed"})
end
Expand Down
36 changes: 36 additions & 0 deletions lib/salad_ui/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,40 @@ defmodule SaladUI.Helpers do
end

def normalize_integer(_), do: nil

@doc """
Variant helper for generating classes based on side and align
"""
@variants %{
side: %{
"top" => "bottom-full mb-2",
"bottom" => "top-full mt-2",
"left" => "right-full mr-2",
"right" => "left-full ml-2"
},
align: %{
"start-horizontal" => "left-0",
"center-horizontal" => "left-1/2 -translate-x-1/2 slide-in-from-left-1/2",
"end-horizontal" => "right-0",
"start-vertical" => "top-0",
"center-vertical" => "top-1/2 -translate-y-1/2 slide-in-from-top-1/2",
"end-vertical" => "bottom-0"
}
}

@spec side_variant(String.t(), String.t()) :: String.t()
def side_variant(side, align \\ "center") do
Enum.map_join(%{side: side, align: align(align, side)}, " ", fn {key, value} -> @variants[key][value] end)
end

# decide align class based on side
defp align(align, side) do
cond do
side in ["top", "bottom"] ->
"#{align}-horizontal"

side in ["left", "right"] ->
"#{align}-vertical"
end
end
end
96 changes: 96 additions & 0 deletions lib/salad_ui/hover_card.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule SaladUI.HoverCard do
@moduledoc """
Implement hover card component
## Usage
<.hover_card>
<.hover_card_trigger>
<.button variant="link">
@salad_ui
</.button>
</.hover_card_trigger>
<.hover_card_content>
Hover card content
</.hover_card_content>
</.hover_card>
"""
use SaladUI, :component

@doc """
Render hover card wrapper
"""
attr(:class, :string, default: nil)
attr(:rest, :global)
slot(:inner_block, required: true)

def hover_card(assigns) do
~H"""
<div
class={
classes([
"inline-block relative group/hover-card",
@class
])
}
{@rest}
>
<%= render_slot(@inner_block) %>
</div>
"""
end

@doc """
Render hover card trigger
"""
attr(:class, :string, default: nil)
attr(:rest, :global)
slot(:inner_block, required: true)

def hover_card_trigger(assigns) do
~H"""
<div
class={
classes([
"",
@class
])
}
{@rest}
>
<%= render_slot(@inner_block) %>
</div>
"""
end

@doc """
Render hover card content
"""
attr(:class, :string, default: nil)
attr(:side, :string, values: ~w(bottom left right top), default: "top")
attr(:align, :string, values: ["start", "center", "end"], default: "center")
attr(:rest, :global)
slot(:inner_block, required: true)

def hover_card_content(assigns) do
assigns =
assign(assigns, :variant_class, side_variant(assigns.side, assigns.align))

~H"""
<div
data-side={@side}
class={
classes([
"absolute hidden group-hover/hover-card:block",
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
@variant_class,
@class
])
}
{@rest}
>
<%= render_slot(@inner_block) %>
</div>
"""
end
end
35 changes: 1 addition & 34 deletions lib/salad_ui/tooltip.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ defmodule SaladUI.Tooltip do

def tooltip_content(assigns) do
assigns =
assigns
|> assign(:variant_class, variant(%{side: assigns.side}))
|> assign(:variant_style, style_variant(%{side: assigns.side}))
assign(assigns, :variant_class, side_variant(assigns.side))

~H"""
<div
Expand All @@ -71,41 +69,10 @@ defmodule SaladUI.Tooltip do
@class
])
}
style={@variant_style}
{@rest}
>
<%= render_slot(@inner_block) %>
</div>
"""
end

@variants %{
side: %{
"top" => "bottom-full mb-2 left-1/2 -translate-x-1/2",
"bottom" => "top-full mt-2 left-1/2 -translate-x-1/2",
"left" => "right-full mr-2 top-1/2 -translate-y-1/2",
"right" => "left-full ml-2 top-1/2 -translate-y-1/2"
}
}

defp variant(variants) do
Enum.map_join(variants, " ", fn {key, value} ->
@variants[key][value]
end)
end

@style_variants %{
side: %{
"top" => "--tw-enter-translate-x: -50%",
"bottom" => "--tw-enter-translate-x: -50%",
"left" => "--tw-enter-translate-y: -50%",
"right" => "--tw-enter-translate-y: -50%"
}
}

defp style_variant(variants) do
Enum.map_join(variants, " ", fn {key, value} ->
@style_variants[key][value]
end)
end
end
4 changes: 1 addition & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,8 @@ defmodule SaladUI.MixProject do
defp deps do
[
{:tails, "~> 0.1.5"},
{:phoenix, "~> 1.7"},
{:phoenix_html, "~> 4.0"},
{:mix_test_watch, "~> 1.2"},
{:phoenix_live_view, "~> 0.20.1"},
{:mix_test_watch, "~> 1.2", only: [:dev, :test]},
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
{:styler, "~> 0.7", only: [:dev, :test], runtime: false},
{:ex_doc, "~> 0.24", only: [:dev, :test], runtime: false},
Expand Down
48 changes: 48 additions & 0 deletions test/salad_ui/helper_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule SaladUI.HelperTest do
use ExUnit.Case, async: true

alias SaladUI.Helpers

@vertical_align_classes %{
"start" => "top-0",
"center" => "top-1/2 -translate-y-1/2 slide-in-from-top-1/2",
"end" => "bottom-0"
}

@horizontal_align_classes %{
"start" => "left-0",
"center" => "left-1/2 -translate-x-1/2 slide-in-from-left-1/2",
"end" => "right-0"
}

@side_classes %{
"top" => "bottom-full mb-2",
"bottom" => "top-full mt-2",
"left" => "right-full mr-2",
"right" => "left-full ml-2"
}

test "build variant for side with default center align" do
for side <- ["top", "bottom"] do
assert Helpers.side_variant(side) =~ @side_classes[side]
assert Helpers.side_variant(side) =~ @horizontal_align_classes["center"]
end

for side <- ["left", "right"] do
assert Helpers.side_variant(side) =~ @side_classes[side]
assert Helpers.side_variant(side) =~ @vertical_align_classes["center"]
end
end

test "build variant with correct allignment for top and bottom" do
for side <- ["top", "bottom"], align <- ["start", "center", "end"] do
assert Helpers.side_variant(side, align) =~ @horizontal_align_classes[align]
end
end

test "build variant with correct allignment for left and right" do
for side <- ["left", "right"], align <- ["start", "center", "end"] do
assert Helpers.side_variant(side, align) =~ @vertical_align_classes[align]
end
end
end

0 comments on commit b5ff577

Please sign in to comment.