Packages
petal_components
1.4.9
4.6.2
4.6.1
4.6.0
4.5.0
4.4.0
4.3.0
4.2.1
4.2.0
4.1.2
4.1.1
4.1.0
4.0.12
4.0.11
4.0.10
4.0.6
4.0.5
4.0.4
4.0.3
4.0.1
3.2.2
3.2.1
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
2.9.3
2.9.2
retired
2.9.1
retired
2.9.0
retired
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.2
2.1.1
2.1.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.9.3
1.9.2
1.9.1
1.9.0
1.8.0
1.7.1
1.7.0
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.0
1.2.14
1.2.13
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.19.10
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.7
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.1
0.14.0
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.0
Shadcn-style Phoenix LiveView components that AI assistants can actually use. Pair with the MCP server so AI coding tools can inspect the real component API.
Current section
Files
Jump to
Current section
Files
lib/petal_components/slide_over.ex
defmodule PetalComponents.SlideOver do
use Phoenix.Component
import PetalComponents.Helpers
alias Phoenix.LiveView.JS
attr(:origin, :string,
default: "right",
values: ["left", "right", "top", "bottom"],
doc: "slideover point of origin"
)
attr(:close_slide_over_target, :string,
default: nil,
doc:
"close_slide_over_target allows you to target a specific live component for the close event to go to. eg: close_slide_over_target={@myself}"
)
attr(:title, :string, default: nil, doc: "slideover title")
attr(:max_width, :string,
default: "md",
values: ["sm", "md", "lg", "xl", "2xl", "full"],
doc: "sets container max-width"
)
attr(:class, :string, default: "", doc: "CSS class")
attr(:rest, :global)
slot(:inner_block, required: false)
def slide_over(assigns) do
~H"""
<div {@rest} id="slide-over">
<div id="slide-over-overlay" class="pc-slideover__overlay" aria-hidden="true"></div>
<div
class={
build_class([
"pc-slideover__wrapper",
get_margin_classes(@origin),
@class
])
}
role="dialog"
aria-modal="true"
>
<div
id="slide-over-content"
class={get_classes(@max_width, @origin, @class)}
phx-click-away={hide_slide_over(@origin, @close_slide_over_target)}
phx-window-keydown={hide_slide_over(@origin, @close_slide_over_target)}
phx-key="escape"
>
<!-- Header -->
<div class="pc-slideover__header">
<div class="pc-slideover__header__container">
<div class="pc-slideover__header__text">
<%= @title %>
</div>
<button
type="button"
phx-click={hide_slide_over(@origin, @close_slide_over_target)}
class="pc-slideover__header__button"
>
<div class="sr-only">Close</div>
<svg class="pc-slideover__header__close-svg">
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
</svg>
</button>
</div>
</div>
<!-- Content -->
<div class="pc-slideover__content">
<%= render_slot(@inner_block) %>
</div>
</div>
</div>
</div>
"""
end
# The live view that calls <.slide_over> will need to handle the "close_slide_over" event. eg:
# def handle_event("close_slide_over", _, socket) do
# {:noreply, push_patch(socket, to: Routes.moderate_users_path(socket, :index))}
# end
def hide_slide_over(origin, close_slide_over_target \\ nil) do
origin_class =
case origin do
x when x in ["left", "right"] -> "translate-x-0"
x when x in ["top", "bottom"] -> "translate-y-0"
end
destination_class =
case origin do
"left" -> "-translate-x-full"
"right" -> "translate-x-full"
"top" -> "-translate-y-full"
"bottom" -> "translate-y-full"
end
js =
JS.remove_class("overflow-hidden", to: "body")
|> JS.hide(
transition: {
"ease-in duration-200",
"opacity-100",
"opacity-0"
},
to: "#slide-over-overlay"
)
|> JS.hide(
transition: {
"ease-in duration-200",
origin_class,
destination_class
},
to: "#slide-over-content"
)
if close_slide_over_target do
JS.push(js, "close_slide_over", target: close_slide_over_target)
else
JS.push(js, "close_slide_over")
end
end
defp get_classes(max_width, origin, class) do
base_classes = "pc-slideover__box"
slide_over_classes =
case origin do
"left" -> "transition translate-x-0"
"right" -> "transition translate-x-0 absolute right-0 inset-y-0"
"top" -> "transition translate-y-0 absolute inset-x-0"
"bottom" -> "transition translate-y-0 absolute inset-x-0 bottom-0"
end
max_width_class =
case origin do
x when x in ["left", "right"] ->
case max_width do
"sm" -> "pc-slideover__box--sm"
"md" -> "pc-slideover__box--md"
"lg" -> "pc-slideover__box--lg"
"xl" -> "pc-slideover__box--xl"
"2xl" -> "pc-slideover__box--2xl"
"full" -> "pc-slideover__box--full"
end
x when x in ["top", "bottom"] ->
""
end
custom_classes = class
build_class([slide_over_classes, max_width_class, base_classes, custom_classes])
end
defp get_margin_classes(margin) do
case margin do
"left" -> "mr-10"
"right" -> "ml-10"
"top" -> "mb-10"
"bottom" -> "mt-10"
end
end
end