• Stars
    star
    164
  • Rank 222,907 (Top 5 %)
  • Language
    Ruby
  • License
    MIT License
  • Created about 1 year ago
  • Updated 25 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The ultimate Turbo / Stimulus / Hotwire modal window for Rails

The Ultimate Turbo Modal for Rails (UTMR)

There are MANY Turbo/Hotwire/Stimulus modal dialog implementations out there, and it seems like everyone goes about it a different way. However, as you may have learned the hard way, the majority fall short in different, often subtle ways. They generally cover the basics quite well, but do not check all the boxes for real-world use.

UTMR aims to be the be-all and end-all of Turbo Modals. I believe it is the best implementation and checks all the boxes. It is feature-rich, yet extremely easy to use.

Under the hood, it uses Stimulus, Turbo, el-transition, and optionally Idiomorph.

It currently ships in a two flavors: Tailwind CSS, and regular, vanilla CSS. It is easy to create your own variant to suit your needs. See lib/ultimate_turbo_modal/flavors/vanilla.rb for an example code.

   

Features and capabilities

  • Extremely easy to use
  • Fully responsive
  • Does not break if a user navigates directly to a page that is usually shown in a modal
  • Opening a modal in a new browser tab (ie: right click) gracefully degrades without having to code a modal and non-modal version of the same page
  • Automatically handles URL history (ie: pushState) for shareable URLs
  • pushState URL optionally overrideable
  • Seamless support for multi-page navigation within the modal
  • Seamless support for forms with validations
  • Seamless support for Rails flash messages
  • Enter/leave animation (fade in/out)
  • Support for long, scrollable modals
  • Properly locks the background page when scrolling a long modal
  • Click outside the modal to dismiss
  • Option to whitelist CSS selectors that won't dismiss the modal when clicked outside the modal (ie: datepicker)
  • Keyboard control; ESC to dismiss
  • Automatic (or not) close button

   

Demo

A demo application can be found at https://github.com/cmer/ultimate_turbo_modal-demo. A video demo can be seen here: https://youtu.be/BVRDXLN1I78.

   

Installation

  1. Install the gem and add to the application's Gemfile by executing:

    $ bundle add ultimate_turbo_modal

  2. Install the npm package:

    $ yarn add ultimate_turbo_modal

    • or -

    $ bin/importmap pin ultimate_turbo_modal

  3. Add the following as the first element in the body tag of views/layouts/application.html.erb:

<%= turbo_frame_tag "modal" %>
  1. Set your desired flavor and default configuration at app/config/initializers/ultimate_turbo_modal.rb.
UltimateTurboModal.configure do |config|
  config.flavor = :tailwind
  config.padding = true
  config.advance = true
  config.close_button = true
  config.header = true
  config.header_divider = true
  config.footer_divider = true
  config.allowed_click_outside_selector = nil
end
  1. Register the Stimulus controller in app/javascript/controllers/index.js adding the following lines at the end.
import setupUltimateTurboModal from "ultimate_turbo_modal";
setupUltimateTurboModal(application);
  1. If using the Tailwind flavor, add the following to tailwind.config.js:
// For npm/yarn
const { getUltimateTurboModalPath } = require('ultimate_turbo_modal/gemPath');

// If using Importmaps, use the following instead:
// const { execSync } = require('child_process');
//
// function getUltimateTurboModalPath() {
//  const path = execSync('bundle show ultimate_turbo_modal').toString().trim();
//  return `${path}/**/*.{erb,html,rb}`;
//}

and then in the content section, add getUltimateTurboModalPath() as follow:

content: [
 './public/*.html',
 './app/helpers/**/*.rb',
 './app/javascript/**/*.js',
 './app/views/**/*.{erb,haml,html,slim,rb}',
 getUltimateTurboModalPath()
]
  1. Optionally (but recommended), configure UTMR to use Idiomorph. See below for details.

   

Usage

  1. Wrap your view inside a modal block as follow:
<%= modal do %>
  Hello World!
<% end %>
  1. Link to your view by specifying modal as the target Turbo Frame:
<%= link_to "Open Modal", "/hello_world", data: { turbo_frame: "modal" } %>

Clicking on the link will automatically open the content of the view inside a modal. If you open the link in a new tab, it will render normally outside of the modal. Nothing to do!

If you need to do something a little bit more advanced when the view is shown outside of a modal, you can use the #inside_modal? method as such:

<% if inside_modal? %>
  <h1 class="text-2xl mb-8">Hello from modal</h1>
<% else %>
  <h1 class="text-2xl mb-8">Hello from a normal page render</h1>
<% end %>

   

Options

padding, default: true

Adds padding inside the modal.

close_button, default: true

Shows or hide a close button (X) at the top right of the modal.

advance, default: true

When opening the modal, the URL in the URL bar will change to the URL of the view being shown in the modal. The Back button dismisses the modal and navigates back.

If a URL is specified as a String, the browser history will advance, and the URL shown in the URL bad will be replaced by the URL specified.

title, default: nil

Title to display in the modal header. Alternatively, you can set the title with a block.

header, default: true

Whether to display a modal header.

header_divider, default: true

Whether to display a divider below the header.

footer_divider, default: true

Whether to display a divider above the footer. The divider will not appear if no footer was specified.

allowed_click_outside_selector, default: nil

A string of CSS selectors that can be clicked outside of the modal without dismissing the modal. Useful for elements such as datepickers.

Example usage with options

<%= modal(padding: true, close_button: false, advance: false) do %>
  Hello World!
<% end %>
<%= modal(padding: true, close_button: false, advance: "/foo/bar") do %>
  Hello World!
<% end %>

Title and Footer

You can set a custom title and footer by passing a block. For example

<%= modal do |m| %>
  <% m.title do %>
    <div>My Title</div>
  <% end %>

  Modal body

  <% m.footer do %>
    <input type="submit" form="myform">Submit</input>
  <% end %>
<% end %>

Installing & Configuring Idiomorph

Idiomorph can morph Turbo Frame responses to allow seemless navigation within Turbo Frames without having to hide and reopen the modal. This is needed to prevent the fade out / fade in animations from repeating when navigating within the modal.

You could optionally not use the code below if you do not intend to allow navigation within the modal.

Note that Turbo 8 will include Idiomorph by default.

In the meantime, add <script src="https://unpkg.com/idiomorph"></script> to your HTML .

And the following code to application.js:

addEventListener("turbo:before-frame-render", (event) => {
  event.detail.render = (currentElement, newElement) => {
    Idiomorph.morph(currentElement, newElement, {
      morphstyle: 'innerHTML'
    })
  }
})

   

Thanks

Thanks to @joeldrapper and @konnorrogers for all the help!

   

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/cmer/ultimate_turbo_modal.

   

License

The gem is available as open source under the terms of the MIT License.

More Repositories

1

socialization

Socialize your app with Likes, Follows and Mentions
Ruby
683
star
2

gigabyte-z390-aorus-master-hackintosh

A guide to build your own Hackintosh based on Gigabyte Z390 Aorus Master
Ruby
476
star
3

lg-tv-control-macos

Automatically wake/sleep and change the input of your LG TV when used as a monitor on macOS
Lua
109
star
4

shoestrap

A simple alternative to Chef and Puppet to bootstrap *nix machines.
Shell
53
star
5

solargraph-rails-init

A simple script to configure Solargraph to work with Rails
Ruby
32
star
6

git-p4-sync

Submit changes made to a Git repository into to Perforce
Ruby
22
star
7

scope_cache_key

Add cache_key functionality to ActiveRecord scopes
Ruby
21
star
8

sendyr

A Ruby interface for the wonderful e-mail newsletter application Sendy
Ruby
20
star
9

alfred-ticktick

Alfred workflow to add tasks to TickTick
Ruby
19
star
10

rails-assets-cdn

CDN support for Rails asset pipeline made easy
Ruby
18
star
11

nobspw

No Bullshit Password strength checker
Ruby
16
star
12

cacheable-csrf-token-rails

Cache HTML containing CSRF protection tokens without worrying
Ruby
15
star
13

ec2-extra-tools

A set of extra command line tools for Amazon EC2
14
star
14

unraid-sanity

A beautiful, sane theme for Unraid 6.6+
CSS
12
star
15

sanitization

Clean & sanitize your ActiveRecord data before saving to database
Ruby
12
star
16

pfsense-bulk-dhcp-import

Import DHCP static mapping from CSV to pfSense
Ruby
10
star
17

forever-internets

Automatically reboot your modem and router when your Internet crashes
Ruby
8
star
18

stimulus-reflex-invoice-beast

Beast-mode for invoice editing
Ruby
8
star
19

landscape

Dead simple server monitoring
Ruby
7
star
20

creality_ender5_config_and_notes

My Creality Ender 5 config files and notes for Marlin, Cura, Octoprint and others
C++
6
star
21

shoestrap-example

A simple Bash framework to bootstrap *nix machines without the headaches
Shell
6
star
22

spree-extra-variant

A Spree extension to add free-form text variants to products without having to create all the different possibilities in the database
JavaScript
6
star
23

postbin.rb

A PostBin.org clone written in Ruby/Sinatra
Ruby
5
star
24

abp_to_pihole_whitelist

AdBlock Plus to PiHole whitelist.txt converter
Ruby
4
star
25

ultimate_turbo_modal-demo

Demo application for Ultimate Turbo Modal for Rails
Ruby
4
star
26

docker-seafile

Shell
3
star
27

enumish

Database-backed Enum for ActiveRecord
Ruby
3
star
28

parsley-payment

Credit card validation for Parsley.js
2
star
29

ec2_userdata

A simple Ruby library that reads UserData on EC2 with graceful fallback when not running on EC2
Ruby
2
star
30

wordpress2tumblr

Import Wordpress posts, comments and images into Tumblr
Ruby
2
star
31

diff_dirs

Ruby helper to diff two directories
Ruby
2
star
32

email_transfer

Script that transfers emails from one server (POP or IMAP) to another (IMAP).
Python
2
star
33

ec2bundlr

EC2Bundlr is a simple and interactive Capistrano task to bundle an EC2 instance into an AMI (Amazon Machine Image)
Ruby
2
star
34

jquery-conditionaldom

ConditionalDom is a jQuery plugin that conditionally allows basic DOM manipulation
CoffeeScript
2
star
35

tplink_smarthome_api

A simple Ruby library to control TP-Link smart plugs
Ruby
2
star
36

cointracking_to_bitcointax

CoinTracking.info to Bitcoin.tax Converter
Ruby
2
star
37

beanstalk_watcher

A simple Sinatra app that monitors Beanstalkd tubes. Possibly to be used in conjunction with a monitoring package such as Monit.
Ruby
2
star
38

twitter_id

Tiny web application that finds the Twitter numeric ID when given a username
Ruby
1
star
39

daemonizr

Process forking and monitoring for mere mortals
1
star
40

validates_child_of

Performs validation on an ActiveRecord object to ensure that it is the child of another one.
Ruby
1
star
41

stimulus-controllers

A collection of useful Stimulus Controllers I wrote
JavaScript
1
star
42

this_person_does_not_exist

Ruby
1
star
43

interactive_brokers_2_tasty_works

Interactive Brokers to TastyWorks Trade Statement Converter in Ruby
Ruby
1
star
44

coin_tracking_rb

A Ruby client for CoinTracking.info
Ruby
1
star
45

karabiner-rules

1
star
46

clusty

No-nonsense EC2 instance and cluster launching
Ruby
1
star
47

awesome-osx

Fantastic software I use and love on macOS/OS X
1
star
48

acts_as_formatted

acts_as_formatted is a plugin that provides basic formatting capabilities to all ActiveRecord column objects of type :string or :text, as long as they have content (nil values will remain nil).
Ruby
1
star