MicroPosts
- Dialog with Rails & Hotwire
At the end of
<body>
:<dialog data-controller="modal-dialog" class="tw-w-[calc(100%-0.5rem)] tw-max-w-xl tw-rounded-lg"> <div class="tw-p-8"> <button data-action="click->modal-dialog#close" class="tw-absolute tw-top-6 tw-right-8 tw-text-5xl tw-opacity-40 hover:tw-opacity-90 tw-select-none" >×</button> <%= turbo_frame_tag 'modal_dialog', data: { action: 'turbo:frame-load->modal-dialog#open' } %> </div> </dialog>
Stimulus controller:
import { Controller } from "@hotwired/stimulus"; export default class extends Controller { connect() { this.element.addEventListener("close", () => { this.element.classList.remove('open') }); } disconnect() { this.close(); } open() { this.element.showModal(); this.element.classList.add('open') } close() { this.element.close(); this.element.classList.remove('open') } }
CSS:
// This scrolls the page to the top, which is a problem if the // user opens the dialog not at the top of the page. // // The following would work but causes an horizontal layout // shift when the scrollbar disappears: // .no-scroll { // overflow: hidden; // } // // Keep an eye on the following thread for better solutions: // https://github.com/whatwg/html/issues/7732 body:has(dialog[open]) { overflow-y: scroll; position: fixed; width: 100%; } dialog { opacity: 0; transition: opacity 0.3s ease-out; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.8); } dialog.open { opacity: 1; } dialog::backdrop { display: none; }
Button that opens the dialog:
<%= link_to( fa_icon('plus', text: 'Invite user'), new_dashboard_organization_team_member_path(@organization), class: '!tw-py-2 !tw-px-3 !tw-text-base btn btn-default', data: { turbo: true, turbo_frame: 'modal_dialog' } ) %>
Form that gets rendered into the dialog:
<div class="container tw-py-20"> <div class="tw-max-w-xl"> <p class="text-muted"> <span data-toggle="tooltip" title="Organization name"><%= @organization.name %></span> / <%= link_to 'Team members', dashboard_organization_team_members_path(@organization) %> </p> <%= turbo_frame_tag 'modal_dialog' do %> <h1 class="tw-m-0 tw-mb-6">Invite team member</h1> <%= render partial: 'form', locals: { team_member_form: @team_member_form, form_method: :post, action_url: dashboard_organization_team_members_path, submit_value: 'Invite' } %> <% end %> </div> </div>
Controller:
def create team_member_form = TeamMemberForm::NewForm.new(new_team_member_params.merge(organization: organization, inviter_email: current_user.email)) if team_member_form.save flash[:success] = 'The user has been added to the team members.' respond_to do |format| format.turbo_stream do render turbo_stream: turbo_stream.action(:redirect, dashboard_organization_team_members_path(organization)) end format.html do redirect_to dashboard_organization_team_members_path(organization) end end else @team_member_form = team_member_form render :new end end
Redirect:
// Workaround for redirecting from within a Turbo Frame: // https://github.com/hotwired/turbo-rails/pull/367 Turbo.StreamActions.redirect = function () { Turbo.visit(this.target); };