Last active
July 21, 2022 21:40
-
-
Save mayel/690a4e27d394888d8ff59c9e0fefe32f to your computer and use it in GitHub Desktop.
reusable modal in Surface/LiveView
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Bonfire.UI.Common.OpenModalLive do | |
@moduledoc """ | |
A button that opens a **modal** | |
""" | |
use Bonfire.UI.Common.Web, :stateful_component | |
alias Bonfire.UI.Common.ReusableModalLive | |
@doc "The title of the modal. Only used if no title slot is passed." | |
prop title_text, :string | |
@doc "The classes of the title of the modal" | |
prop title_class, :css_class, default: "font-bold text-base" | |
@doc "The classes of the close/cancel button on the modal. Only used if no close_btn slot is passed." | |
prop cancel_btn_class, :css_class, default: "btn btn-outline btn-sm normal-case" | |
@doc "Force modal to be open" | |
prop show, :boolean, default: false | |
prop form_opts, :any, default: [] | |
@doc "Optional prop to hide the actions at the bottom of the modal" | |
prop no_actions, :boolean, default: false | |
@doc "The classes of the title of the modal" | |
prop reusable_modal_id, :string, default: "modal" | |
@doc """ | |
Additional attributes to add onto the modal wrapper | |
""" | |
prop opts, :keyword, default: [] | |
@doc """ | |
Slots for the contents of the modal, title, buttons... | |
""" | |
slot default | |
slot open_btn | |
slot action_btns | |
slot cancel_btn | |
slot title | |
def open() do | |
set(show: true) | |
end | |
def close() do | |
set(show: false) | |
end | |
def set(assigns) when is_list(assigns) do | |
send_update(ReusableModalLive, Keyword.put(assigns, :id, e(assigns, :reusable_modal_id, "modal"))) | |
end | |
def set(assigns) when is_map(assigns) do | |
send_update(ReusableModalLive, Map.put(assigns, :id, e(assigns, :reusable_modal_id, "modal"))) | |
end | |
# Default event handlers | |
def handle_event("open", _, socket) do | |
socket = socket | |
|> assign(show: true) | |
set(socket.assigns) # copy all of this component's assigns to the reusable modal (including slots!) | |
{:noreply, socket} | |
end | |
def handle_event("close", _, socket) do | |
close() | |
{:noreply, socket} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<button | |
:on-click={"open"} | |
type="button" | |
> | |
<#slot name="open_btn"> | |
<button class={@open_btn_class} type="button"> | |
{@open_btn_text || "Open modal"} | |
</button> | |
</#slot> | |
</button> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Bonfire.UI.Common.ReusableModalLive do | |
use Bonfire.UI.Common.Web, :stateful_component | |
@moduledoc """ | |
A reusable **modal**. This component is meant to be included only once, typically in the app's live layout. | |
""" | |
@doc "The title of the modal. Only used if no title slot is passed." | |
prop title_text, :string, default: nil | |
@doc "The classes of the title of the modal" | |
prop title_class, :css_class, default: | |
@doc "The classes of the close/cancel button on the modal. Only used if no close_btn slot is passed." | |
prop cancel_btn_class, :css_class, default: nil | |
@doc "Force modal to be open" | |
prop show, :boolean, default: false | |
prop form_opts, :any, default: [] | |
@doc "Optional prop to hide the actions at the bottom of the modal" | |
prop no_actions, :boolean, default: false | |
@doc """ | |
Additional attributes to add onto the modal wrapper | |
""" | |
prop opts, :keyword, default: [] | |
@doc """ | |
Slots for the contents of the modal, title, buttons... | |
""" | |
slot default | |
slot open_btn | |
slot action_btns | |
slot cancel_btn | |
slot title | |
def mount(socket) do | |
# need this because ReusableModalLive when called in the HEEX layout doesn't set Surface defaults | |
{:ok, socket | |
|> assign( | |
title_text: nil, | |
title_class: nil, | |
cancel_btn_class: nil, | |
show: false, | |
form_opts: [], | |
no_actions: false, | |
opts: [] | |
) | |
} | |
end | |
def handle_event("close", _, socket) do | |
{:noreply, assign(socket, show: false)} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div | |
id={@id} | |
class={"modal max-h-[100%] ", | |
"modal-closed": !@show, | |
"modal-open": @show} | |
:attrs={@opts}> | |
<form {...@form_opts}> | |
<div | |
phx-click="close" | |
phx-target={"##{@id}"} | |
class="fixed inset-0 transition-opacity bg-slate-600/60 backdrop-blur-md" | |
aria-hidden="true"> | |
</div> | |
<div class="relative border-4 modal-box border-base-content border-opacity-20 max-w-[100%] min-w-[320px] min-h-[100px]"> | |
<h3 class={@title_class || "font-bold text-base"}> | |
<#slot name="title">{@title_text}</#slot> | |
</h3> | |
<div class="absolute top-0 right-0 block pt-5 pr-4"> | |
<button | |
phx-click="close" | |
phx-target={"##{@id}"} | |
type="button" | |
class="normal-case btn btn-ghost btn-circle btn-sm"> | |
<span class="sr-only">{l "Close"}</span> | |
<Outline.XIcon class="w-4 h-4" /> | |
</button> | |
</div> | |
<div class="mt-2" data-id="modal-contents"> | |
<#slot></#slot> | |
</div> | |
<div | |
:if={!@no_actions} | |
class="modal-action"> | |
<#slot name="action_btns"></#slot> | |
<div :on-click="close"> | |
<#slot name="cancel_btn"> | |
<button class={@cancel_btn_class || "btn btn-outline btn-sm normal-case"} type="button">{l "Cancel"}</button> | |
</#slot> | |
</div> | |
</div> | |
</div> | |
</form> | |
</div> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment