#How can I move the modal component to the body ?
14 messages · Page 1 of 1 (latest)
I have a layout that is seperated as topbar sidebar and main layout, the modal is in the main layout so it renders in a position that I dont want
but the default CSS for the default modal component renders it absolute and centered in the browser, it doesn't matter where it is actually rendered in the HTML
Yeap, I also tried fixed position but that didn't seem to work..
Sounds like needs a refactor ?
have you tried the dialog element?
the hook you showed above is just going to break a lot of things and I wouldn't trust doing things like that. In a normal phoenix app using default components, it doesn't matter where in the DOM the modal is rendered, it will always appear absolutely positioned and in the center of the browser
if this isn't how your modal looks, then you must have broken the styling
here is the smallest repro
<div class="flex h-40 lg:h-32 ">
<!-- Topbar -->
<div class="flex ">
</div>
</div>
<!-- Main Layout -->
<div id="app" class="flex flex-grow">
<CanaryWeb.Navigation.navigation current_user={@current_user} current_tenant={@current_tenant} />
<!-- Main Content -->
<main class="bg-stone-50 flex flex-col -translate-y-12 lg:-translate-y-16 px-6 py-4 rounded-xl w-full max-w-full">
<%= @inner_content %>
</main>
</div>
Nope but i can give it a try asap
Just realized that I should't do it with a help from a friend but not sure what to come up with other than refactoring (as you told I must break the styling)
This is what I use:
attr :id, :string, required: true
attr :title, :string
attr :class, :string, default: ""
attr :cancel_text, :string, default: "Cancel"
attr :cancel_class, :string, default: "outline"
slot(:inner_block, required: true)
slot(:action_row)
def modal_dialog(assigns) do
~H"""
<dialog id={@id} class={["modal", @class]} data-keep="open" data-keep="open">
<div
class="modal-content modal--md pane"
phx-click-away={close_modal_dialog(@id)}
phx-window-keydown={close_modal_dialog(@id)}
phx-key="escape"
>
<div class="modal-header">
<h2><%= @title %></h2>
<a href="#" class="phx-modal-close" phx-click={close_modal_dialog(@id)}> </a>
</div>
<%= render_slot(@inner_block) %>
<%= if assigns[:action_row] do %>
<div class="confirmation-buttons">
<button type="reset" class={["button push-right ", @cancel_class]} phx-click={close_modal_dialog(@id)}>
<%= @cancel_text %>
</button>
<%= render_slot(@action_row) %>
</div>
<% end %>
</div>
</dialog>
"""
end
def close_modal_dialog(id) do
JS.dispatch("felt:close_modal", detail: %{id: id})
end
def close_modal_dialog(%Phoenix.LiveView.Socket{} = socket, id) do
Phoenix.LiveView.push_event(socket, "app:close_modal", %{id: id})
end
def show_modal_dialog(id) do
JS.dispatch("felt:show_modal", detail: %{id: id})
end
def show_modal_dialog(%Phoenix.LiveView.Socket{} = socket, id) do
Phoenix.LiveView.push_event(socket, "app:show_modal", %{id: id})
end
const showModal = (event: CustomEvent) => {
const modalElement = document.getElementById(
event.detail.id as string
) as HTMLDialogElement | null;
if (modalElement) {
modalElement.showModal();
}
};
const closeModal = (event: CustomEvent) => {
const modalElement = document.getElementById(
event.detail.id as string
) as HTMLDialogElement | null;
if (modalElement) {
modalElement.close();
}
};
window.addEventListener("app:show_modal", showModal as EventListener);
window.addEventListener("phx:app:show_modal", showModal as EventListener);
window.addEventListener("app:close_modal", closeModal as EventListener);
window.addEventListener("phx:app:close_modal", closeModal as EventListener);
And a config for new LiveSocket to avoid re-renders from closing it:
dom: {
onBeforeElUpdated(from, to) {
if (from.matches("[data-keep]")) {
for (const attr of from.attributes) {
if (from.dataset.keep?.split(" ").includes(attr.name)) {
to.setAttribute(attr.name, attr.value);
}
}
}
return true;
},
},
works great with the one exception that you need to be a bit careful if you put it in eg a dropdown where it will be hidden when the dropdown loses focus
Usage example:
<.form :let={f} id="update-name-form" for={@update_name_form} phx-submit="update_name">
<.modal_dialog id="update-name-modal" title="Change name">
<div phx-feedback-for={input_name(f, :name)}>
<%= text_input(f, :name,
phx_hook: "autoFocus",
placeholder: "name",
phx_debounce: "blur"
) %>
<%= label(f, :name, "Name") %>
<%= error_tag(f, :email) %>
</div>
<:action_row>
<%= submit("Save", class: "button pink", phx_disable_with: "Saving...") %>
</:action_row>
</.modal_dialog>
</.form>
thanks, looks promising but I will definitely use a custom dropdown in it but I can give it a try